好像又有一个星期没更博客了。。
最近疯狂考试。。。唯一有点收获的就是学会了莫队这种神奇的算法。。
听起来很难。。其实是一个很简单的东西。。
就是在区间处理问题时对于一个待求区间[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 MBSubmit: 309 Solved: 205
[Submit][Status][Discuss]
Description
小B有一个序列,包含N个1~K之间的整数。他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数。小B请你帮助他回答询问。
Input
第一行,三个整数N、M、K。
第二行,N个整数,表示小B的序列。
接下来的M行,每行两个整数L、R。
Output
M行,每行一个整数,其中第i行的整数表示第i个询问的答案。
Sample Input
6 4 3
1 3 2 1 1 3
1 4
2 6
3 5
5 6
1 3 2 1 1 3
1 4
2 6
3 5
5 6
Sample Output
6
9
5
2
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左右。。
其他差不多。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 6 using namespace std; 7 8 #define maxn 50001 9 10 int n,cnt=1,team[maxn],num[maxn],a[maxn]; 11 12 long long up[maxn],down[maxn]; 13 14 struct ed{ 15 int l,r,id; 16 }edge[maxn]; 17 18 bool cmp(const ed A,const ed B) 19 { 20 if(team[A.l]==team[B.l]) 21 return A.r<B.r; 22 return team[A.l]<team[B.l]; 23 } 24 25 void build() 26 { 27 int T=sqrt(n); 28 for(int i=1;i<=n;i++) 29 { 30 if(i%T==0)cnt++; 31 team[i]=cnt; 32 } 33 } 34 35 long long gcd(long long nn,long long mm){return nn%mm==0?mm:gcd(mm,nn%mm);} 36 37 inline int read() 38 { 39 int x=0;char ch=getchar(); 40 while(ch<'0'||ch>'9')ch=getchar(); 41 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); 42 return x; 43 } 44 45 int main() 46 { 47 int m,k; 48 n=read(),m=read(),k=read(); 49 for(int i=1;i<=n;i++)a[i]=read(); 50 build(); 51 for(int i=1;i<=m;i++) 52 { 53 edge[i].l=read(),edge[i].r=read(); 54 edge[i].id=i; 55 } 56 sort(1+edge,1+edge+m,cmp); 57 int ll=1,lr=0; 58 long long ans=0; 59 for(int i=1;i<=m;i++) 60 { 61 if(edge[i].l==edge[i].r) 62 { 63 up[edge[i].id]=1,down[edge[i].id]=1; 64 continue; 65 } 66 if(lr<edge[i].r) 67 { 68 for(int j=lr+1;j<=edge[i].r;j++) 69 ans+=num[a[j]]*2+1,num[a[j]]++; 70 } 71 else 72 { 73 for(int j=lr;j>edge[i].r;j--) 74 ans-=(--num[a[j]])*2+1; 75 } 76 lr=edge[i].r; 77 if(ll>edge[i].l) 78 { 79 for(int j=ll-1;j>=edge[i].l;j--) 80 ans+=num[a[j]]*2+1,num[a[j]]++; 81 } 82 else 83 { 84 for(int j=ll;j<edge[i].l;j++) 85 ans-=(--num[a[j]])*2+1; 86 } 87 ll=edge[i].l; 88 up[edge[i].id]=ans; 89 } 90 for(int i=1;i<=m;i++) 91 printf("%lld\n",up[i]); 92 return 0; 93 }