【莫队】bzoj 3781,bzoj 2038,bzoj 3289
好像又有一个星期没更博客了。。
最近疯狂考试。。。唯一有点收获的就是学会了莫队这种神奇的算法。。
听起来很难。。其实是一个很简单的东西。。
就是在区间处理问题时对于一个待求区间[L',R']通过之前求出的[L,R]更新[L,R+1],[L+1,R],[L,R-1],[L,R-1]的方式弄出答案[L,R]。
比如求【3,5】 我们知道了【1,7】,那么我们这样转化 : 【1,7】--> 【2,7】--> 【3,7】 --> 【3,6】 --> 【3,5】而求得。
那怎么确定从哪个区间转移呢?
在这里我们可以把区间左端点和右端点排个序,然后全局变量L,R代表当前答案区间,ans代表当前答案。。每次对于一个新询问慢慢转移即可。
但是这样貌似还是太暴力了。。
有两个优化:1) 二维曼哈顿生成树,太难写。。不推荐。
2)分块。把区间左端点分块处理。每次处理一个块。
关于分块复杂度的证明http://blog.csdn.net/bossup/article/details/39236275
其中每次转移可能是O(1)或者O(logn)。
下面列出一些题目。。
BZOJ 3781 小B的袜子
3781: 小B的询问
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 309 Solved: 205
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
1 3 2 1 1 3
1 4
2 6
3 5
5 6
Sample Output
9
5
2
HINT
对于全部的数据,1<=N、M、K<=50000
基础的莫队算法。
在这里主要说说转移
一般大家的转移应该都是ans-=num[a[i]]*num[a[i]],num[a[i]]++,ans+=num[a[i]]*num[a[i]].这样大约是2400ms左右
这里说种更好的转移:ans+=num[a[i]++]*2+1.这样大约可以跑到1200ms左右。。
其他差不多。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm> using namespace std; #define maxn 50001 int n,cnt=,team[maxn],num[maxn],a[maxn]; long long up[maxn],down[maxn]; struct ed{
int l,r,id;
}edge[maxn]; bool cmp(const ed A,const ed B)
{
if(team[A.l]==team[B.l])
return A.r<B.r;
return team[A.l]<team[B.l];
} void build()
{
int T=sqrt(n);
for(int i=;i<=n;i++)
{
if(i%T==)cnt++;
team[i]=cnt;
}
} long long gcd(long long nn,long long mm){return nn%mm==?mm:gcd(mm,nn%mm);} inline int read()
{
int x=;char ch=getchar();
while(ch<''||ch>'')ch=getchar();
while(ch>=''&&ch<='')x=x*+ch-'',ch=getchar();
return x;
} int main()
{
int m,k;
n=read(),m=read(),k=read();
for(int i=;i<=n;i++)a[i]=read();
build();
for(int i=;i<=m;i++)
{
edge[i].l=read(),edge[i].r=read();
edge[i].id=i;
}
sort(+edge,+edge+m,cmp);
int ll=,lr=;
long long ans=;
for(int i=;i<=m;i++)
{
if(edge[i].l==edge[i].r)
{
up[edge[i].id]=,down[edge[i].id]=;
continue;
}
if(lr<edge[i].r)
{
for(int j=lr+;j<=edge[i].r;j++)
ans+=num[a[j]]*+,num[a[j]]++;
}
else
{
for(int j=lr;j>edge[i].r;j--)
ans-=(--num[a[j]])*+;
}
lr=edge[i].r;
if(ll>edge[i].l)
{
for(int j=ll-;j>=edge[i].l;j--)
ans+=num[a[j]]*+,num[a[j]]++;
}
else
{
for(int j=ll;j<edge[i].l;j++)
ans-=(--num[a[j]])*+;
}
ll=edge[i].l;
up[edge[i].id]=ans;
}
for(int i=;i<=m;i++)
printf("%lld\n",up[i]);
return ;
}
bzoj 2038 [2009国家集训队]小Z的袜子(hose)
2038: [2009国家集训队]小Z的袜子(hose)
Time Limit: 20 Sec Memory Limit: 259 MB
Submit: 4490 Solved: 2062
[Submit][Status][Discuss]
Description
作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。
Input
输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。
Output
包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例)
Sample Input
1 2 3 3 3 2
2 6
1 3
3 5
1 6
Sample Output
0/1
1/1
4/15
【样例解释】
询问1:共C(5,2)=10种可能,其中抽出两个2有1种可能,抽出两个3有3种可能,概率为(1+3)/10=4/10=2/5。
询问2:共C(3,2)=3种可能,无法抽到颜色相同的袜子,概率为0/3=0/1。
询问3:共C(3,2)=3种可能,均为抽出两个3,概率为3/3=1/1。
注:上述C(a, b)表示组合数,组合数C(a, b)等价于在a个不同的物品中选取b个的选取方案数。
【数据规模和约定】
30%的数据中 N,M ≤ 5000;
60%的数据中 N,M ≤ 25000;
100%的数据中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm> using namespace std; #define maxn 50001 int n,cnt=,team[maxn],num[maxn],a[maxn]; long long up[maxn],down[maxn]; struct ed{
int l,r,id;
}edge[maxn]; bool cmp(const ed A,const ed B)
{
if(team[A.l]==team[B.l])
return A.r<B.r;
return team[A.l]<team[B.l];
} void build()
{
int T=sqrt(n);
for(int i=;i<=n;i++)
{
if(i%T==)cnt++;
team[i]=cnt;
}
} long long gcd(long long nn,long long mm){return nn%mm==?mm:gcd(mm,nn%mm);} inline int read()
{
int x=;char ch=getchar();
while(ch<''||ch>'')ch=getchar();
while(ch>=''&&ch<='')x=x*+ch-'',ch=getchar();
return x;
} int main()
{
int m;
n=read(),m=read();
for(int i=;i<=n;i++)a[i]=read();
build();
for(int i=;i<=m;i++)
{
scanf("%d%d",&edge[i].l,&edge[i].r);
edge[i].id=i;
}
sort(+edge,+edge+m,cmp);
int ll=,lr=;
long long ans=;
for(int i=;i<=m;i++)
{
if(edge[i].l==edge[i].r)
{
up[edge[i].id]=,down[edge[i].id]=;
continue;
}
if(lr<edge[i].r)
{
for(int j=lr+;j<=edge[i].r;j++)
ans+=num[a[j]]*+,num[a[j]]++;
}
else
{
for(int j=lr;j>edge[i].r;j--)
ans-=(--num[a[j]])*+;
}
lr=edge[i].r;
if(ll>edge[i].l)
{
for(int j=ll-;j>=edge[i].l;j--)
ans+=num[a[j]]*+,num[a[j]]++;
}
else
{
for(int j=ll;j<edge[i].l;j++)
ans-=(--num[a[j]])*+;
}
ll=edge[i].l;
long long aa=ans-(lr-ll+);
long long bb=(long long)(lr-ll)*(lr-ll+);
long long cc=gcd(aa,bb);
up[edge[i].id]=aa/cc,down[edge[i].id]=bb/cc;
}
for(int i=;i<=m;i++)
printf("%lld/%lld\n",up[i],down[i]);
return ;
}
bzoj 3289 Mato的文件管理
3289: Mato的文件管理
Time Limit: 40 Sec Memory Limit: 128 MB
Submit: 1084 Solved: 479
[Submit][Status][Discuss]
Description
Input
第一行一个正整数n,表示Mato的资料份数。
第二行由空格隔开的n个正整数,第i个表示编号为i的资料的大小。
第三行一个正整数q,表示Mato会看几天资料。
之后q行每行两个正整数l、r,表示Mato这天看[l,r]区间的文件。
Output
q行,每行一个正整数,表示Mato这天需要交换的次数。
Sample Input
1 4 2 3
2
1 2
2 4
Sample Output
2
HINT
Hint
n,q <= 50000
样例解释:第一天,Mato不需要交换
第二天,Mato可以把2号交换2次移到最后。
和前面的差不多。。
只不过转移要logn
加个什么树状数组维护一下就可以了。。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath> #define maxn 50001 using namespace std; inline int in()
{
int x=;char ch=getchar();
while(ch>''||ch<'')ch=getchar();
while(ch>=''&&ch<='')x=x*+ch-'',ch=getchar();
return x;
} struct ed{
int l,r,id;
}edge[maxn],edge1[maxn]; int a[maxn],n,pos[maxn],MST[maxn]; long long ans=,an[maxn]; bool cmp(const ed A,const ed B)
{
if(pos[A.l]==pos[B.l])
return A.r<B.r;
return pos[A.l]<pos[B.l];
} void build()
{
int T=sqrt(n),res=;
for(int i=;i<=n;i++)
{
if(i%T==)res++;
pos[i]=res;
}
} void add(int d,int pos)
{
while(pos<=n)
{
MST[pos]+=d;
pos+=pos&-pos;
}
} int b[maxn]; int sum(int pos)
{
int sum=;
while(pos)
{
sum+=MST[pos];
pos-=pos&-pos;
}
return sum;
} void pre()
{
for(int i=;i<=n;i++)edge1[i].id=i,edge1[i].r=a[i];
sort(+edge1,+edge1+n,cmp);
for(int i=;i<=n;i++)a[edge1[i].id]=i;
} int main()
{
int q;
n=in();
for(int i=;i<=n;i++)a[i]=in();
pre();
q=in();
build();
for(int i=;i<=q;i++)edge[i].l=in(),edge[i].r=in(),edge[i].id=i;
sort(+edge,+edge+q,cmp);
int ll=,lr=;
for(int i=;i<=q;i++)
{
if(lr<edge[i].r)for(int j=lr+;j<=edge[i].r;j++)ans+=j-ll-sum(a[j]),add(,a[j]);
else for(int j=lr;j>edge[i].r;j--)add(-,a[j]),ans-=j-ll-sum(a[j]);
lr=edge[i].r;
if(ll>edge[i].l)for(int j=ll-;j>=edge[i].l;j--)ans+=sum(a[j]-),add(,a[j]);
else for(int j=ll;j<edge[i].l;j++)add(-,a[j]),ans-=sum(a[j]-);
ll=edge[i].l;
an[edge[i].id]=ans;
}
for(int i=;i<=q;i++)printf("%lld\n",an[i]);
return ;
}
【莫队】bzoj 3781,bzoj 2038,bzoj 3289的更多相关文章
- BZOJ2038【莫队算法】
THE FIRST 莫队算法. /************************************************************** Problem: 2038 User: ...
- BZOJ 3289 Mato的文件管理(莫队+离散化求逆序数)
3289: Mato的文件管理 Time Limit: 40 Sec Memory Limit: 128 MB Submit: 2171 Solved: 891 [Submit][Status][ ...
- bzoj 2038 A-小Z的袜子[hose] - 莫队算法
作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从 ...
- BZOJ 3289: Mato的文件管理[莫队算法 树状数组]
3289: Mato的文件管理 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 2399 Solved: 988[Submit][Status][Di ...
- BZOJ 2038: [2009国家集训队]小Z的袜子(hose) [莫队算法]【学习笔记】
2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 7687 Solved: 3516[Subm ...
- BZOJ 3289: Mato的文件管理 莫队+BIT
3289: Mato的文件管理 Description Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号.为了防止他人偷拷,这些资料都是加密过的 ...
- BZOJ 2038 小z的袜子 & 莫队算法(不就是个暴力么..)
题意: 给一段序列,询问一个区间,求出区间中.....woc! 贴原题! 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过 ...
- 【BZOJ】3289: Mato的文件管理(莫队算法+树状数组)
http://www.lydsy.com/JudgeOnline/problem.php?id=3289 很裸的莫队... 离线了区间然后分块排序后,询问时搞搞就行了. 本题中,如果知道$[l, r] ...
- 【BZOJ】2038: [2009国家集训队]小Z的袜子(hose)(组合计数+概率+莫队算法+分块)
http://www.lydsy.com/JudgeOnline/problem.php?id=2038 学了下莫队,挺神的orzzzz 首先推公式的话很简单吧... 看的题解是从http://for ...
随机推荐
- HttpClient(4.3.5) - ResponseHandler
The simplest and the most convenient way to handle responses is by using the ResponseHandler interfa ...
- unity3d首次倒入工程文件出错Opening file Library/FailedAssetImports.txt failed解决方法
打开unity3d,首次倒入工程到unity编辑器,但是频繁弹出“Opening file Library/FailedAssetImports.txt failed”的错误对话框,很麻烦. 解决方法 ...
- JAVA解析xml的五种方式比较
1)DOM解析 DOM是html和xml的应用程序接口(API),以层次结构(类似于树型)来组织节点和信息片段,映射XML文档的结构,允许获取 和操作文档的任意部分,是W3C的官方标准 [优点] ① ...
- 代码优化—From <effective C++>
1.尽可能的延后变量定义式的出现时间 不止应该延后变量的定义,直到非得使用该变量的前一刻为止,甚至应该尝试延后这份定义直到能够给它初值实参为止. 如果这样不仅能够避免构造和析构非必要对象,还可以避免无 ...
- C++ 宽字符(wchar_t)与窄字符(char)的转换
了解 长度 宽字符wchar_t的长度16位,可以用来显示中文等除英文外的其他文字, 窄字符 char 的长度 8 位,只能处理英文. 哪里可以见到 在VS2010, 2012, 2013 ...
- poj 1286 Necklace of Beads
这是做的第一道群论题,自然要很水又很裸.注意用long long. 就是用到了两个定理 burnside :不等价方案数=每个置换的不动置换方案数的和 / 置换个数 polya: 一个置换的不动置换方 ...
- shadow服务端、客户端配置流程
服务端 系统环境 CentOS 7 64位,由于系统自带python,shadowsocks服务端我们选择python版,过程如下 yum install python-setuptools & ...
- style、currentStyle、getComputedStyle区别介绍
style.currentStyle.getComputedStyle区别介绍 来自:蓝色天空 样式表有三种方式 内嵌样式(inline Style) :是写在Tag里面的,内嵌样式只对所有的Tag有 ...
- linux的SVN搭建与同步
以下的配置文件,开头都不能有空格 安装: yum install subversion 验证:svnserve --version 代码库创建:(类似,可以建立多个仓库)[repo]mkdir -p ...
- js判断屏幕分辨率的代码
通过下面的代码判断分辨率 <script language="JavaScript"> <!-- Begin function redirectPage() { ...