BZOJ3052: [wc2013]糖果公园
http://www.lydsy.com/JudgeOnline/problem.php?id=3052
树上的带修改莫队算法。
按左端点所在块为第一关键字,右端点所在块为第二关键字,时间为第三关键字,排序。然后进行树上莫队,每次询问经过修改或逆修改来使时间倒流或前进。
复杂度证明:
设block_num为块数,block_size为块的大小,则有block_num×block_size=n,在证明中我们假设n,q同阶。
设块对(block_i,block_j),易知这样的块对不会超过block_num2个。
对于块对内的操作:我们考虑总复杂度,左端点共移动至多O(q×block_size),右端点亦是。时间共移动至多O(block_num2×q)。故这一部分的复杂度为O(n×(block_size+block_num2))。
对于块与块之间的操作,不超过block_num2次:左端第移动一次,最多O(n),右端点亦是如此。时间最多移动O(q)=O(n)。故这一部分复杂度为O(block_num2×n)。
故总复杂度为O(n×(block_size+block_num2))。
可以证明当block_size=n2/3时,block_num=n1/3,复杂度最优,为O(n5/3)。
#include<bits/stdc++.h> using namespace std; typedef long long int64; const int maxn=100015,maxe=200015,maxm=100015,maxq=100015,maxk=20; struct Tmodify{int x,v;}M[maxq]; struct Tquery{int i,u,v,t;}Q[maxq]; int tot,now[maxn],pre[maxe],son[maxe]; int n,m,q,lim,tim,ask,v[maxm],w[maxn],c[maxn]; inline void connect(int u,int v){pre[++tot]=now[u];now[u]=tot;son[tot]=v;} inline void read(int &x){ char c; for (c=getchar();c<'0'||c>'9';c=getchar()); for (x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0'; } void init(){ read(n);read(m);read(q);lim=log2(n); for (int i=1;i<=m;++i) read(v[i]); for (int i=1;i<=n;++i) read(w[i]); for (int i=1,u,v;i<=n-1;++i){read(u);read(v);connect(u,v);connect(v,u);} for (int i=1;i<=n;++i) read(c[i]); for (int t,x,y,i=1;i<=q;++i){ read(t);read(x);read(y); switch (t){ case 0:M[++tim]=(Tmodify){x,y};break; case 1:Q[++ask]=(Tquery){ask,x,y,tim};break; } } } int dep[maxn],anc[maxn][maxk]; void get_anc(int u,int f){ anc[u][0]=f;dep[u]=dep[f]+1; for (int k=1;k<=lim;++k) anc[u][k]=anc[anc[u][k-1]][k-1]; for (int p=now[u];p;p=pre[p]) if (son[p]!=f) get_anc(son[p],u); } int siz,cnt,top,stk[maxn],bel[maxn]; void get_block(int u,int f){ for (int bot=top,p=now[u];p;p=pre[p]) if (son[p]!=f){ get_block(son[p],u); if (top-bot>=siz) for(++cnt;top!=bot;bel[stk[top--]]=cnt); } stk[++top]=u; } void prepare(){ siz=(int)pow(n,0.6); get_anc(1,0);get_block(1,0); for (int i=1;i<=top;++i) bel[stk[i]]=cnt; for (int i=1;i<=ask;++i) if (bel[Q[i].u]>bel[Q[i].v]) swap(Q[i].u,Q[i].v); } inline bool cmp(const Tquery &x,const Tquery &y){ if (bel[x.u]!=bel[y.u]) return bel[x.u]<bel[y.u]; else if (bel[x.v]!=bel[y.v]) return bel[x.v]<bel[y.v]; else return x.t<y.t; } bool exist[maxn]; int64 res,ans[maxq];int sum[maxm]; inline void xor_node(int x){ if (exist[x]) res-=1ll*v[c[x]]*w[sum[c[x]]--]; else res+=1ll*v[c[x]]*w[++sum[c[x]]]; exist[x]^=1; } inline void xor_path(int u,int v){ if (dep[u]<dep[v]) swap(u,v); while (dep[u]!=dep[v]){xor_node(u);u=anc[u][0];} while (u!=v){xor_node(u);xor_node(v);u=anc[u][0];v=anc[v][0];} } inline void modify(int k){ int x=M[k].x,ever=c[x],now=M[k].v; if (exist[x]){ res-=1ll*v[ever]*w[sum[ever]--]; res+=1ll*v[now]*w[++sum[now]]; } swap(c[x],M[k].v); } inline void move_time(int ever,int now){ for (int i=ever+1;i<=now;++i) modify(i); for (int i=ever;i>=now+1;--i) modify(i); } inline int lca(int u,int v){ if (dep[u]<dep[v]) swap(u,v); for (int i=0,h=dep[u]-dep[v];h;++i,h>>=1) if (h&1) u=anc[u][i]; for (int k=lim;k>=0;--k) if (anc[u][k]!=anc[v][k]){u=anc[u][k];v=anc[v][k];} return u==v?u:anc[u][0]; } inline void solve(int k){ xor_path(Q[k-1].u,Q[k].u); xor_path(Q[k-1].v,Q[k].v); move_time(Q[k-1].t,Q[k].t); int x=lca(Q[k].u,Q[k].v); xor_node(x);ans[Q[k].i]=res;xor_node(x); } void work(){ prepare();sort(Q+1,Q+ask+1,cmp); Q[0]=(Tquery){0,1,1,0};for (int i=1;i<=ask;++i) solve(i); for (int i=1;i<=ask;++i) printf("%lld\n",ans[i]); } int main(){ init(); work(); return 0; }