洛谷P4074/UOJ58[WC2013]糖果公园(莫队)
莫队之集大成者——树上带修改莫队。
首先对树dfs,记下进入和离开一个结点的时间戳$in_u$,$out_u$,并得到相应的长为$2n$的dfs序(常称为括号序),对于一个询问$(u,v)$:
- 若$lca_{u,v}=u$,则$in_u$到$in_v$之间$(u,v)$路径上的点恰好出现一次,其他点要么不出现,要么出现2次(进入一次,退出一次),$lca_{u,v}=v$同理;
- 否则,不妨设$out_u<in_v$(否则交换$u$,$v$即可),则$[out_u,in_v]$区间内$u$到$lca_{u,v}$的点出现一次(只有退出),$v$到$lca_{u,v}$的点出现一次(只有进入),其他点为0次或2次。但是注意,$lca_{u,v}$不在序列中出现,要单独保存。
那么就可以莫队维护了,每次记下$vis_u$表示$u$是否被算在贡献中,如果重复则删去,就可以找出所有出现一次的点,即u,v路径上的点。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; typedef long long ll; const int N=100050; char rB[1<<21],*rS,*rT,wB[(1<<21)+50]; int wp=-1; inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;} inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;} inline int rd(){ char c=gc(); while(c<48||c>57)c=gc(); int x=c&15; for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15); return x; } short buf[25]; inline void wt(ll x){ if(wp>(1<<21))flush(); short l=-1; while(x>9){ buf[++l]=x%10; x/=10; } wB[++wp]=x|48; while(l>=0)wB[++wp]=buf[l--]|48; wB[++wp]='\n'; } int G[N],to[N<<1],nxt[N<<1],sz=0,f[18][N],dep[N],din[N],dout[N],pos[N<<1],dfsc=0,a[N],w[N],val[N],ps[N],pre[N],cur[N],K,cnt[N]; ll s[N],ans=0ll; bool vis[N]; struct query{ int l,r,t,p,id; }Q[N]; inline bool cmp(query a,query b){ if((a.l-1)/K!=(b.l-1)/K)return (a.l-1)/K<(b.l-1)/K; if((a.r-1)/K!=(b.r-1)/K)return (a.r-1)/K<(b.r-1)/K; return a.t<b.t; } inline void adde(int u,int v){ to[++sz]=v;nxt[sz]=G[u];G[u]=sz; to[++sz]=u;nxt[sz]=G[v];G[v]=sz; } void dfs(int u,int fa){ int i,v; dep[u]=dep[f[0][u]=fa]+1; for(i=1;(1<<i)<dep[u];++i)f[i][u]=f[i-1][f[i-1][u]]; pos[din[u]=++dfsc]=u; for(i=G[u];i;i=nxt[i])if((v=to[i])!=fa)dfs(v,u); pos[dout[u]=++dfsc]=u; } inline int lca(int u,int v){ short i; if(dep[u]<dep[v])swap(u,v); for(i=16;i>=0;--i)if(dep[u]-(1<<i)>=dep[v])u=f[i][u]; if(u==v)return u; for(i=16;i>=0;--i)if(f[i][u]!=f[i][v]){u=f[i][u];v=f[i][v];} return f[0][u]; } inline void add(int c){ ans+=(ll)val[c]*w[++cnt[c]]; } inline void del(int c){ ans-=(ll)val[c]*w[cnt[c]--]; } inline void ins(int p){ if(vis[p])del(a[p]); else add(a[p]); vis[p]^=1; } int main(){ int n=rd(),m=rd(),q=rd(),mt=0,mq=0,i,u,v,opt,l,x,y,tim; for(i=1;i<=m;++i)val[i]=rd(); for(i=1;i<=n;++i)w[i]=rd(); for(i=1;i<n;++i){ u=rd();v=rd(); adde(u,v); } dfs(1,0); for(i=1;i<=n;++i)a[i]=rd(); for(i=0;i<q;++i){ opt=rd();u=rd();v=rd(); if(!opt){ps[++mt]=u;pre[mt]=a[u];a[u]=cur[mt]=v;} else{ if(dep[u]>dep[v])swap(u,v); l=lca(u,v); if(l==u){Q[mq].l=din[u];Q[mq].r=din[v];} else{ if(dout[u]>din[v])swap(u,v); Q[mq].l=dout[u];Q[mq].r=din[v];Q[mq].p=l; } Q[mq].t=mt;Q[mq].id=mq;++mq; } } K=pow(n,2.0/3); sort(Q,Q+mq,cmp); for(i=mt,tim=Q[0].t;i>tim;--i)a[ps[i]]=pre[i]; for(i=x=Q[0].l,y=Q[0].r;i<=y;++i)ins(pos[i]); if(Q[0].p)ins(Q[0].p); s[Q[0].id]=ans; if(Q[0].p)ins(Q[0].p); for(i=1;i<mq;++i){ for(;x<Q[i].l;++x)ins(pos[x]); while(x>Q[i].l)ins(pos[--x]); for(;y>Q[i].r;--y)ins(pos[y]); while(y<Q[i].r)ins(pos[++y]); while(tim<Q[i].t){ ++tim; if(vis[ps[tim]]){del(pre[tim]);add(cur[tim]);} a[ps[tim]]=cur[tim]; } for(;tim>Q[i].t;--tim){ if(vis[ps[tim]]){del(cur[tim]);add(pre[tim]);} a[ps[tim]]=pre[tim]; } if(Q[i].p)ins(Q[i].p); s[Q[i].id]=ans; if(Q[i].p)ins(Q[i].p); } for(i=0;i<mq;++i)wt(s[i]); flush(); return 0; }