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;

}

  

 

你可能感兴趣的:(ZOJ)