[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 ",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