BZOJ2038 [2009国家集训队]小Z的袜子(hose)(莫队算法)
神奇的莫队算法,用来解决可离线无修改的区间查询问题:
- 首先对原序列进行分块,√n块每块√n个;
- 然后对所有查询的区间[l,r]进行排序,首先按l所在的块序号升序排序,如果一样就按r升序排序;
- 最后就按顺序一个一个求出各个查询的结果:知道[l,r]的答案,并且在此基础上能在比较快地在O(x)得到相邻区间[l+1,r]、[l-1,r]、[l,r-1]、[l,r+1]的答案,那样就能从[l,r]的基础上对lr加加减减得到任意一个区间[l',r']的答案。
看似暴力,但这样做的时间复杂度是O(x*n*√n) !因为:
- l是按其所在块序号排列,同一块里面一次最多√n次++l或--l到达目标;一块最多大概√n次加加减减;总共√n块;所以l改变的次数顶多也就√n*√n*√n。
- r在同一块是升序的,所以同一块最多n次++r;下一块时r假设在上一块到达最远,那最多n次--r回到目标;总共√n块;所以r改变次数顶多也就(n+n)*√n。
- 而每次加加减减转移新答案的代价是x,所以时间复杂度是O(x*n*√n) !
这一题,设每个区间[l,r]各个颜色的袜子数分别为$a,b,c,d,\dots$,每个区间[l,r]的答案就是$(C_a^2+C_b^2+C_c^2+C_d^2+\cdots)/C_{r-l+1}^2$,展开化简得:
$$(a^2+b^2+c^2+d^2+\cdots-a-b-c-d-\cdots)/((r-l+1)*(r-l+1-1))$$
$$(a^2+b^2+c^2+d^2+\cdots-(r-l+1))/((r-l+1)*(r-l))$$
其中$(a^2+b^2+c^2+d^2+\cdots)$便可作为莫队算法处理的区间答案,开个数组记录abcd...的个数可以在O(1)转移到相邻区间。
另外特判区间l=r的情况。。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 using namespace std; 6 #define MAXN 55555 7 8 int block; 9 struct Query{ 10 int i,l,r; 11 bool operator<(const Query &q)const{ 12 if(l/block==q.l/block) return r<q.r; 13 return l/block<q.l/block; 14 } 15 }query[MAXN]; 16 17 long long gcd(long long a,long long b){ 18 if(b==0) return a; 19 return gcd(b,a%b); 20 } 21 22 int seq[MAXN]; 23 long long cnt[MAXN],ansx[MAXN],ansy[MAXN]; 24 void insert(long long &res,int a){ 25 res-=cnt[a]*cnt[a]; 26 ++cnt[a]; 27 res+=cnt[a]*cnt[a]; 28 } 29 void remove(long long &res,int a){ 30 res-=cnt[a]*cnt[a]; 31 --cnt[a]; 32 res+=cnt[a]*cnt[a]; 33 } 34 int main(){ 35 int n,m; 36 scanf("%d%d",&n,&m); 37 block=sqrt(n); 38 for(int i=1; i<=n; ++i) scanf("%d",seq+i); 39 for(int i=0; i<m; ++i){ 40 query[i].i=i; 41 scanf("%d%d",&query[i].l,&query[i].r); 42 } 43 sort(query,query+m); 44 int l=1,r=1; 45 ++cnt[seq[1]]; 46 long long res=1; 47 for(int i=0; i<m; ++i){ 48 if(query[i].l==query[i].r){ 49 ansx[query[i].i]=0; ansy[query[i].i]=1; 50 continue; 51 } 52 while(l<query[i].l){ 53 remove(res,seq[l]); 54 ++l; 55 } 56 while(l>query[i].l){ 57 --l; 58 insert(res,seq[l]); 59 } 60 while(r<query[i].r){ 61 ++r; 62 insert(res,seq[r]); 63 } 64 while(r>query[i].r){ 65 remove(res,seq[r]); 66 --r; 67 } 68 long long a=res-(query[i].r-query[i].l+1),b=(query[i].r-query[i].l+1LL)*(query[i].r-query[i].l),c=gcd(b,a); 69 ansx[query[i].i]=a/c; ansy[query[i].i]=b/c; 70 } 71 for(int i=0; i<m; ++i) printf("%lld/%lld\n",ansx[i],ansy[i]); 72 return 0; 73 }