BZOJ3589 : 動態樹

Claris發表於2014-05-16

對於既要支援子樹修改又要支援鏈查詢,

需要樹鏈剖分

然後求出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;
}

  

 

相關文章