BZOJ3052 & UOJ58:WC2013糖果公园——题解
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ3052 & UOJ58:WC2013糖果公园——题解相关的知识,希望对你有一定的参考价值。
http://www.lydsy.com/JudgeOnline/problem.php?id=3052
输入格式
输出格式
input
4 3 5
1 9 2
7 6 5 1
2 3
3 1
3 4
1 2 3 2
1 1 2
1 4 2
0 2 1
1 1 2
1 4 2
output
84
131
27
84
——————————————————————————————————————
这题对于一个刚学莫队的人来说……挺萌的。
首先先对树分块,具体请看我的前一篇博客王室联邦我们就可以知道,假设我们要分块,我们设预期分块大小为s,则所有块的大小可为[s,3s]。
按照这种分块方法分块即可,注意为了我们算法的速度,分块大小s=n的2/3次方,证明可看小兔大佬的博客中单点修改莫队。
(dfs同时预处理LCA所需要的几个数据,以后会用)
在那之后按照莫队的思路为询问排序,然后开始我们正式的算法。
(排序时0号点的l和r都设成1(看到下面的操作之后就会知道这样做干什么了))
这里说一下个别几个数组的含义。
1.sta[i]:i是否在当前路径上,是为1.
2.last[i]:第i个操作为修改时,修改前的颜色。
3.cc[i]:最终i点的颜色。
4.col[i]:当前操作时i点的颜色。
再说一个函数rev(i)表示将i这个点在我们的路径上被添加/删除。
剩下的应该都能看得懂,我就不说什么了。
首先按照排序后顺序扫询问,我们得到了我们当前的询问和前一个询问。
那么首先我们需要将前一个询问的id和后一个询问的id中间的修改操作修改了。
然后就是最神奇的操作了,solve操作的证明详见vfk的博客。
(当然你也可以通过画图肉眼观察法证明,以下简述solve内部操作)
我们把当前的左询问节点l和之前的左询问节点l0之间的最短路取反。(当然同时对右节点也是一样)
然后取反左右询问节点的LCA,再更新cur,在取反LCA,这样我们就得到了这个询问的答案了。
#include<cstdio> #include<stack> #include<cctype> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int N=100001; const int INF=2147483647; inline ll read(){ ll X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch==\'-\';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ int to; int nxt; }edge[N*2]; struct qu{ int id,l,r,bl,br; }qry[N]; int n,m,Q,q,s,cnt,head[N]; int top,idx,stk[N],blk[N]; int anc[N][20],dep[N]; int v[N],cc[N],col[N],sum[N]; int a[N],b[N],op[N],last[N]; ll cur,ans[N],w[N]; bool sta[N]; inline void add(int u,int v){ cnt++; edge[cnt].to=v; edge[cnt].nxt=head[u]; head[u]=cnt; return; } bool cmp(qu d,qu e){ if(d.bl!=e.bl)return d.bl<e.bl; if(d.br!=e.br)return d.br<e.br; return d.id<e.id; } void dfs(int u){ int st=top; dep[u]=dep[anc[u][0]]+1; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(v==anc[u][0])continue; anc[v][0]=u; dfs(v); if(top-st>=s){ idx++; while(top>st)blk[stk[top--]]=idx; } } stk[++top]=u; return; } int LCA(int i,int j){ if(dep[i]<dep[j])swap(i,j); for(int k=17;k>=0;k--){ if(dep[anc[i][k]]>=dep[j])i=anc[i][k]; } if(i==j)return i; for(int k=17;k>=0;k--){ if(anc[i][k]!=anc[j][k])i=anc[i][k],j=anc[j][k]; } return anc[i][0]; } void init(){ n=read();m=read();Q=read();s=pow(n,2.0/3.0); for(int i=1;i<=m;i++)v[i]=read(); for(int i=1;i<=n;i++)w[i]=read()+w[i-1]; for(int i=1;i<n;i++){ int u=read(),v=read(); add(u,v);add(v,u); } for(int i=1;i<=n;i++)cc[i]=col[i]=read(); dfs(1); while(top)blk[stk[top--]]=idx; for(int j=1;j<=17;j++){ for(int i=1;i<=n;i++){ anc[i][j]=anc[anc[i][j-1]][j-1]; } } return; } inline void rev(int x){ cur-=w[sum[col[x]]]*v[col[x]]; sta[x]?sum[col[x]]--:sum[col[x]]++; sta[x]=!sta[x]; cur+=w[sum[col[x]]]*v[col[x]]; return; } inline void solve(int x,int y){ int l=LCA(x,y); while(x!=l)rev(x),x=anc[x][0]; while(y!=l)rev(y),y=anc[y][0]; return; } inline void modify(int x,int y){ if(!sta[x]){ col[x]=y; return; } rev(x); col[x]=y; rev(x); return; } inline void upt(int tarT,int curT){ while(curT<tarT){ curT++; if(!op[curT])modify(a[curT],b[curT]); } while(curT>tarT){ if(!op[curT])modify(a[curT],last[curT]); curT--; } return; } int main(){ init(); for(int i=1;i<=Q;i++){ op[i]=read(),a[i]=read(),b[i]=read(); if(op[i]){ qry[++q].id=i; if(blk[a[i]]>blk[b[i]])swap(a[i],b[i]); qry[q].l=a[i];qry[q].r=b[i]; qry[q].bl=blk[a[i]];qry[q].br=blk[b[i]]; }else last[i]=cc[a[i]],cc[a[i]]=b[i]; } sort(qry+1,qry+q+1,cmp); qry[0].l=qry[0].r=1; for(int i=1;i<=q;i++){ upt(qry[i].id,qry[i-1].id); solve(qry[i].l,qry[i-1].l);solve(qry[i].r,qry[i-1].r); int l=LCA(qry[i].l,qry[i].r); rev(l); ans[qry[i].id]=cur; rev(l); } for(int i=1;i<=Q;i++){ if(op[i])printf("%lld\\n",ans[i]); } return 0; }
以上是关于BZOJ3052 & UOJ58:WC2013糖果公园——题解的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 3052: [wc2013]糖果公园 | 树上莫队
BZOJ3052: [wc2013]糖果公园 树分块+待修改莫队算法