BZOJ3553 : [Shoi2014]三叉神经树

设val[i]为i连出去的树突中输出值为0的个数

如果val[x]<=1,输出值为1,否则输出值为0

修改x就相当于val[f[i]]++或者val[f[i]]--

 

用Link-cut Tree维护这棵树,

每个节点维护val[x]、size[x](子树大小)、cnt1[x](子树里val[x]==1的个数)、cnt2[x](子树里val[x]==2的个数)

 

以val[f[i]]--为例:

设x=f[i]

access(x)取出x到根这条链

如果val[x]!=2,那么x的输出值不变,直接修改x即可

如果val[x]==2,那么影响到的肯定是一段连续的2以及最开头的2之前的那个点y

如果从根节点开始到x一直都是2,那么直接把这条链打上全部修改为1的标记即可

否则,因为这个具有单调性,所以可以二分这个y,

二分时方便起见二分深度,

对于当前二分到的mid,把深度为mid的点t splay上来

那么只要检验t的右子树是否符合cnt2[]==size[]就可以了

找到y之后splay(y),然后y的右子树里打上全部修改为1的标记,再把y单点修改即可

 

总复杂度$O(q\log^2n)$

 

#include<cstdio>
#define N 500010
int f[N*3],son[N][2],size[N],val[N],cnt1[N],cnt2[N],tag[N],a[N];
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline void swap(int&a,int&b){int c=a;a=b;b=c;}
inline bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
inline void same1(int x,int p){
  if(!x)return;
  val[x]=tag[x]=p;
  if(p==1)cnt1[x]=size[x],cnt2[x]=0;else cnt2[x]=size[x],cnt1[x]=0;
}
inline void pb(int x){if(tag[x])same1(son[x][0],tag[x]),same1(son[x][1],tag[x]),tag[x]=0;}
inline void up(int x){
  size[x]=size[son[x][0]]+size[son[x][1]]+1;
  cnt1[x]=cnt1[son[x][0]]+cnt1[son[x][1]]+(val[x]==1);
  cnt2[x]=cnt2[son[x][0]]+cnt2[son[x][1]]+(val[x]==2);
}
inline void rotate(int x){
  int y=f[x],w=son[y][1]==x;
  son[y][w]=son[x][w^1];
  if(son[x][w^1])f[son[x][w^1]]=y;
  if(f[y]){
    int z=f[y];
    if(son[z][0]==y)son[z][0]=x;
    if(son[z][1]==y)son[z][1]=x;
  }
  f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
}
inline void splay(int x){
  int s=1,i=x,y;a[1]=i;
  while(!isroot(i))a[++s]=i=f[i];
  while(s)pb(a[s--]);
  while(!isroot(x)){
    y=f[x];
    if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
    rotate(x);
  }
  up(x);
}
inline void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
inline int kth(int x,int k){
  while(1){
    if(k==size[son[x][0]]+1)return x;
    if(k<=size[son[x][0]])x=son[x][0];else k-=size[son[x][0]]+1,x=son[x][1];
  }
}
inline void dec(int x){
  access(x);splay(x);
  if(x==1||val[x]!=2){val[x]--;up(x);return;}
  if(size[x]==cnt2[x]){same1(x,1);return;}
  int y=0,l=1,r=size[x]-1,mid,t=x;
  while(l<=r){
    mid=(l+r)>>1;
    splay(t=kth(t,mid));
    if(cnt2[son[t][1]]==size[son[t][1]])y=t,r=mid-1;else l=mid+1;
  }
  splay(y);same1(son[y][1],1);val[y]--;up(y);
}
inline void inc(int x){
  access(x);splay(x);
  if(x==1||val[x]!=1){val[x]++;up(x);return;}
  if(size[x]==cnt1[x]){same1(x,2);return;}
  int y=0,l=1,r=size[x]-1,mid,t=x;
  while(l<=r){
    mid=(l+r)>>1;
    splay(t=kth(t,mid));
    if(cnt1[son[t][1]]==size[son[t][1]])y=t,r=mid-1;else l=mid+1;
  }
  splay(y);same1(son[y][1],2);val[y]++;up(y);
}
int n,i,x,j,g[N*3],nxt[N*6],v[N*6],ed,q,show[N*3];
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x,int pre){
  f[x]=pre;
  if(x>n)return;
  for(int i=g[x];i;i=nxt[i])if(v[i]!=pre){
    dfs(v[i],x);
    if(!show[v[i]])val[x]++;
  }
  show[x]=val[x]<=1;
}
int main(){
  for(read(n),i=1;i<=n;i++)for(size[i]=1,j=0;j<3;j++)read(x),add(i,x),add(x,i);
  for(i=n+1;i<=3*n+1;i++)read(show[i]);
  dfs(1,0);
  read(q);
  while(q--){
    read(x);
    if(show[x])inc(f[x]);else dec(f[x]);show[x]^=1;
    splay(1);
    printf("%d\n",val[1]<=1);
  }
  return 0;
}

  

 

转载于:https://www.cnblogs.com/clrs97/p/4403251.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值