莫队

 [2009国家集训队]小Z的袜子

https://www.lydsy.com/JudgeOnline/problem.php?id=2038

 1 /****************************************************
 2     Time:704 ms
 3     Memory:3648 kb
 4 ****************************************************/
 5 
 6 const int N=50005;
 7 struct que
 8 {
 9     int l,r,z;
10 }a[N],b[N];
11 int col[N],c[N];
12 ll fx[N],fy[N];
13 int n,m,T,p,cc,t;
14  
15 bool cmp1(que x,que y){return x.l<y.l;}
16 bool cmp2(que x,que y){return x.r<y.r;}
17 ll gcd(ll x,ll y) { return y==0 ? x : gcd(y,x%y) ; }
18  
19 void calc(int s,int t,int dt)
20 {
21     FOR(i,s,t)
22     {
23         cc-=(ll)c[col[i]]*(c[col[i]]-1);
24         c[col[i]]+=dt;
25         cc+=(ll)c[col[i]]*(c[col[i]]-1);
26     }
27 }
28  
29 int main()
30 {
31     scanf("%d%d",&n,&m);
32     FOR(i,1,n) scanf("%d",&col[i]);
33     FOR(i,1,m) scanf("%d%d",&a[i].l,&a[i].r),a[i].z=i;
34     sort(a+1,a+m+1,cmp1);
35     //FOR(i,1,m) cerr<<a[i].l<<" "<<a[i].r<<" "<<a[i].z<<endl;
36     T=sqrt(n*1.0);p=n/T+(n%T!=0);
37     //cerr<<T<<" "<<p<<endl;
38     for(rg int i=1,j=1;i<=p;++i)
39     {
40         for(t=0;j<=m && a[j].l>(i-1)*T && a[j].l<=i*T ; ++j) b[++t]=a[j];
41         sort(b+1,b+t+1,cmp2);
42         //cerr<<i<<" "<<j<<endl;
43         //FOR(hhh,1,t) cerr << b[hhh].l << " "<< b[hhh].r << " "<< b[hhh].z << " "<<endl;
44         int tl=b[1].l,tr=b[1].l-1;cc=0;
45         memset(c,0,sizeof(c));
46         FOR(k,1,t)
47         {
48             if(tl<b[k].l) calc(tl,b[k].l-1,-1);
49             else if(tl>b[k].l) calc(b[k].l,tl-1,1);
50             if(tr<b[k].r) calc(tr+1,b[k].r,1);
51             else if(tr>b[k].r) calc(b[k].r+1,tr,-1);
52             fx[b[k].z]=cc;fy[b[k].z]=b[k].r-b[k].l+1;
53             tl=b[k].l,tr=b[k].r;
54         }
55     }
56     FOR(i,1,m)
57     {
58         if((!fx[i])||(!fy[i])) {puts("0/1"); continue;}
59         fy[i]=fy[i]*(fy[i]-1);
60         cc=gcd(fx[i],fy[i]);
61         printf("%lld/%lld\n",fx[i]/cc,fy[i]/cc);
62     }
63     return 0;
64 }

 

 

 

带修改莫队

 

普通莫队是不能带修改的

我们可以强行让它可以修改,就像DP一样,可以强行加上一维时间维,表示这次操作的时间。

即把询问 [l,r] 变成 [l,r,time]

那么我们的坐标也可以在时间维上移动,即 [l,r,time] 多了一维可以移动的方向,可以变成:

 

[l-1,r,time][l+1,r,time][l,r-1,time][l,r+1,time][l,r,time-1][l,r,time+1]

 

这样的转移也是 O(1) 的,但是我们排序又多了一个关键字,再搞搞就行了

可以用和普通莫队类似的方法排序转移,做到

这一次我们排序的方式是以

为一块,分成了

块,第一关键字是左端点所在块,第二关键字是右端点所在块,第三关键字是时间。

还是来证明一下时间复杂度(默认块大小为

):

 

左右端点所在块不变,时间在排序后单调向右移,这样的复杂度是 O(n)若左右端点所在块改变,时间一次最多会移动n个格子,时间复杂度 O(n)左端点所在块一共有

中,右端点也是

种,一共

种,每种乘上移动的复杂度 O(n) ,总复杂度

 

树上莫队

莫队只能处理线性问题,我们要把树强行压成一维的

我们可以将树的括号序跑下来,把括号序分块,在括号序上跑莫队

具体怎么做呢?

dfs一棵树,然后如果dfs到x点,就push_back(x),dfs完x点,就直接push_back(-x),然后我们在挪动指针的时候

 

新加入的值是x ---> add(x)新加入的值是-x ---> del(x)新删除的值是x ---> del(x)新删除的值是-x ---> add(x)

 

这样的话,我们就把一棵树处理成了序列。

好像还有对树的连通块分块的方法,不过好像比较难我也不会(

例题是[WC2013]糖果公园,这题是带修改树上莫队

题意是给你一棵树,每个点有颜色,每次询问

val表示该颜色的价值

cnt表示颜色出现的次数

w表示该颜色出现i次后的价值

先把树变成序列,然后每次添加/删除一个点,这个点的对答案的的贡献是可以在 O(1) 时间内获得的,即

发现因为他会把起点的子树也扫了一遍,产生多余的贡献,怎么办呢?

因为扫的过程中起点的子树里的点肯定会被扫两次,但贡献为0

所以可以开一个vis数组,每次扫到点x,就把 vis_x 异或上1

如果 vis_x=0 ,那这个点的贡献就可以不计

所以可以用树上莫队来求

修改的话,加上一维时间维即可,变成带修改树上莫队

然后因为所包含的区间内可能没有LCA,对于没有的情况要将多余的贡献删除,然后就完事了

 

参考资料

https://baijiahao.baidu.com/s?id=1611364100257622493&wfr=spider&for=pc

posted @ 2019-03-19 15:24  universeplayer  阅读(172)  评论(0编辑  收藏  举报