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 }

 

posted @ 2016-02-29 10:41  WABoss  阅读(920)  评论(0编辑  收藏  举报