BZOJ3589 : 动态树
对于既要支持子树修改又要支持链查询,
需要树链剖分
然后求出DFS序,DFS的时候先DFS重儿子,
然后子树是1个区间,链是$O(\log n)$个区间
这道题对于查询若干条链的并:
由于K<=5,所以考虑容斥原理
转化为查询若干条链的交,
假设有5条链ABCDE要求交
可以先求AB的交T,再求TC的交…
考虑如何求两条树链的交:
本题中树链保证是父亲到儿子
设两条链为(a,b)(x,y),b是a的父亲,y是x的父亲
保存的交是(a',b')
c=lca(a,x)
如果c比b高或者c比y高,那么交集为空
否则a'=c
如果y在b的下面,那么b'=y,否则b'=b
每次查询$O(2^k(k\log n+\log^2n))$
常数优化:
因为对$2^{31}$取模,所以直接用int自然溢出即可,可快一倍
#include<cstdio> #include<algorithm> #define N 200010 #define K 17 using namespace std; int n,i,q,x,y,k,op,ed,g[N],v[N<<1],nxt[N<<1],st[N],en[N],dfn,d[N],f[N][18],son[N],size[N],top[N],ques[6][2],ans; inline void read(int&a){ char c;bool f=0;a=0; while(!((((c=getchar())>='0')&&(c<='9'))||(c=='-'))); if(c!='-')a=c-'0';else f=1; while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0'; if(f)a=-a; } inline void addedge(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline int lca(int x,int y){ if(x==1||y==1)return 1; if(x==y)return x; if(d[x]<d[y])swap(x,y); for(int i=K;~i;i--)if(d[f[x][i]]>=d[y])x=f[x][i]; if(x==y)return x; for(int i=K;~i;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } void dfs1(int x,int pre){ size[x]=1;d[x]=d[pre]+1; int heavy=0,sizemax=0,i; for(f[x][0]=pre,i=1;i<=K;i++)f[x][i]=f[f[x][i-1]][i-1]; for(i=g[x];i;i=nxt[i])if(v[i]!=pre){ dfs1(v[i],x),size[x]+=size[v[i]]; if(size[v[i]]>sizemax)sizemax=size[v[i]],heavy=v[i]; } if(heavy)son[x]=heavy; } void dfs2(int x,int pre,int t){ st[x]=++dfn;top[x]=t; if(son[x])dfs2(son[x],x,t); for(int i=g[x];i;i=nxt[i])if(v[i]!=pre&&v[i]!=son[x])dfs2(v[i],x,v[i]); en[x]=dfn; } int tot,l[N<<1],r[N<<1],len[N<<1],val[N<<1],tag[N<<1]; int build(int a,int b){ int x=++tot; len[x]=b-a+1; if(a==b)return x; int mid=(a+b)>>1; l[x]=build(a,mid);r[x]=build(mid+1,b); return x; } inline void add1(int x,int p){if(!x)return;val[x]+=len[x]*p;tag[x]+=p;} inline void pb(int x){if(tag[x]!=0)add1(l[x],tag[x]),add1(r[x],tag[x]),tag[x]=0;} inline void up(int x){val[x]=val[l[x]]+val[r[x]];} void add(int x,int a,int b,int c,int d,int p){ if(c<=a&&b<=d){add1(x,p);return;} int mid=(a+b)>>1; pb(x); if(c<=mid)add(l[x],a,mid,c,d,p); if(d>mid)add(r[x],mid+1,b,c,d,p); up(x); } int ask(int x,int a,int b,int c,int d){ if(c<=a&&b<=d)return val[x]; int mid=(a+b)>>1,t=0; pb(x); if(c<=mid)t+=ask(l[x],a,mid,c,d); if(d>mid)t+=ask(r[x],mid+1,b,c,d); up(x); return t; } inline int query(int x,int y){ if(x<1)return 0; int t=0; while(top[x]!=top[y])t+=ask(1,1,n,st[top[x]],st[x]),x=f[top[x]][0]; return t+ask(1,1,n,st[y],st[x]); } inline void merge(int&a,int&b,int x,int y){ if(a==0)return; if(a==-1){a=x,b=y;return;} int c=lca(a,x); if(d[c]<d[b]||d[c]<d[y]){a=b=0;return;} a=c; if(d[b]<d[y])b=y; } void dfs(int x,int a,int b,int o){ merge(a,b,ques[x][0],ques[x][1]); int t=query(a,b); if(!o)ans-=t;else ans+=t; for(x++;x<=k;x++)dfs(x,a,b,o^1); } int main(){ read(n); for(i=1;i<n;i++)read(x),read(y),addedge(x,y),addedge(y,x); dfs1(1,0);dfs2(1,0,1); build(1,n); read(q); while(q--){ read(op); if(op){ read(k); for(i=1;i<=k;i++){ read(ques[i][0]),read(ques[i][1]); if(d[ques[i][0]]<d[ques[i][1]])swap(ques[i][0],ques[i][1]); } ans=0; for(i=1;i<=k;i++)dfs(i,-1,-1,1); if(ans<0)ans+=(~0U>>1)+1; printf("%d\n",ans); }else{ read(x),read(y); add(1,1,n,st[x],en[x],y); } } return 0; }