题意:
有一棵三叉树,n个非叶节点。叶节点有0或1,非叶节点的值为3个孩子中较多的值。q个操作修改某个叶节点的值,问根的值。
n≤500000,q≤500000
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#define N 510000
using namespace std;
struct node{int s1,s2,s3,tp,fa;}t[3*N];
struct node1{int l,r,lc,rc,rev,p;}lt[2*N];
int s[3*N],siz[N],n,m,dfn[N],id,w[N],tl;
bool b[N];
void dfs(int x)
{
siz[x]=1;
if(t[x].s1<=n)
{
dfs(t[x].s1);
siz[x]+=siz[t[x].s1];
}
if(t[x].s2<=n)
{
dfs(t[x].s2);
siz[x]+=siz[t[x].s2];
if(siz[t[x].s2]>siz[t[x].s1]) swap(t[x].s1,t[x].s2);
}
if(t[x].s3<=n)
{
dfs(t[x].s3);
siz[x]+=siz[t[x].s3];
if(siz[t[x].s3]>siz[t[x].s1]) swap(t[x].s1,t[x].s3);
}
}
void dfs1(int x,int fa,int tp)
{
dfn[x]=++id;w[dfn[x]]=x;t[x].tp=tp;
if(t[x].s1<=n) dfs1(t[x].s1,x,tp);
if(t[x].s2<=n) dfs1(t[x].s2,x,t[x].s2);
if(t[x].s3<=n) dfs1(t[x].s3,x,t[x].s3);
}
void pre_s(int x)
{
if(t[x].s1<=n) pre_s(t[x].s1);
if(t[x].s2<=n) pre_s(t[x].s2);
if(t[x].s3<=n) pre_s(t[x].s3);
int cnt[2];cnt[0]=cnt[1]=0;
cnt[s[t[x].s1]]++;cnt[s[t[x].s2]]++;cnt[s[t[x].s3]]++;
if(cnt[0]>cnt[1]) s[x]=0;
else s[x]=1;
if(cnt[s[t[x].s1]]==2) b[x]=1;
}
void upd(int now)
{
int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2;
if(lt[rc].p==-1) {lt[now].p=-1;return;}
if(lt[lc].p==-1 || lt[rc].p!=mid+1) lt[now].p=lt[rc].p;
else lt[now].p=lt[lc].p;
}
void bt(int l,int r)
{
int now=++tl;
lt[now].l=l;lt[now].r=r;
if(l<r)
{
int mid=(l+r)/2;
lt[now].lc=tl+1;bt(l,mid);
lt[now].rc=tl+1;bt(mid+1,r);
upd(now);
}
else
{
if(b[w[l]]) lt[now].p=l;
else lt[now].p=-1;
}
}
int find(int now,int k)
{
int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2;
if(lt[now].l==lt[now].r) return lt[now].rev;
if(mid>=k) return find(lc,k)^lt[now].rev;
else return find(rc,k)^lt[now].rev;
}
void get_s(int x,int *c)
{
int o;c[0]=c[1]=0;
o=s[t[x].s1];
if(t[x].s1<=n) o^=find(1,dfn[t[x].s1]);
c[o]++;
o=s[t[x].s2];
if(t[x].s2<=n) o^=find(1,dfn[t[x].s2]);
c[o]++;
o=s[t[x].s3];
if(t[x].s3<=n) o^=find(1,dfn[t[x].s3]);
c[o]++;
}
int query(int now,int l,int r)
{
int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2;
if(lt[now].l==l && lt[now].r==r) return lt[now].p;
if(mid>=r) return query(lc,l,r);
else if(l>mid) return query(rc,l,r);
else
{
int p1,p2=query(rc,mid+1,r);
if(p2!=mid+1) return p2;
p1=query(lc,l,mid);
if(p1==-1) return p2;
return p1;
}
}
void reverse(int now,int l,int r)
{
int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2;
if(lt[now].l==l && lt[now].r==r) {lt[now].rev^=1;return;}
if(mid>=r) reverse(lc,l,r);
else if(l>mid) reverse(rc,l,r);
else reverse(lc,l,mid),reverse(rc,mid+1,r);
}
void change(int now,int k,int o)
{
int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2;
if(lt[now].l==lt[now].r) {if(o) lt[now].p=k;else lt[now].p=-1;return;}
if(mid>=k) change(lc,k,o);
else change(rc,k,o);
upd(now);
}
void cal(int x,int *c)
{
int o=s[t[x].s1];
if(t[x].s1<=n) o^=find(1,dfn[t[x].s1]);
if(c[o]==2) change(1,dfn[x],1);
else change(1,dfn[x],0);
}
void modify(int x)
{
int fx=t[x].tp,c[2];
s[x]^=1;
while(x)
{
if(x!=fx)
{
int p=query(1,dfn[fx],dfn[x]-1);
if(p==-1) break;
reverse(1,p,dfn[x]-1);
if(w[p]!=fx) break;
}
x=t[fx].fa;fx=t[x].tp;
if(x==0) break;
get_s(x,c);cal(x,c);
int o=s[x]^find(1,dfn[x]);
if(c[o]>c[o^1]) break;
s[x]^=1;
}
}
void solve()
{
scanf("%d",&m);
while(m--)
{
int x,c[2];scanf("%d",&x);
get_s(t[x].fa,c);
if(c[s[x]]==2) modify(t[x].fa);
s[x]^=1;
get_s(t[x].fa,c);cal(t[x].fa,c);
printf("%d\n",s[1]^find(1,1));
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&t[i].s1,&t[i].s2,&t[i].s3);
t[t[i].s1].fa=t[t[i].s2].fa=t[t[i].s3].fa=i;
}
dfs(1);
dfs1(1,0,1);
for(int i=n+1;i<=3*n+1;i++) scanf("%d",&s[i]);
pre_s(1);
bt(1,n);
solve();
return 0;
}
题解:
感觉这是属于那一类很妙的树剖的(套路)。。
易知某个点x修改颜色影响fa的条件是fa的3个孩子中有2个是x的颜色,而且注意如果x修改前会影响fa,那修改后依然会影响fa。因为x颜色的总是有2个。同理如果修改前影响不到,那修改后也影响不到。
每次修改影响的都是自底向上连续的一段,那就可以考虑用树剖。x维护的信息是x的重孩子的修改会不会影响x。
对于一段重链,我们确定底端的点要修改了,然后就自底向上找一段最长的会被重孩子影响的,给这一段颜色取反。
如果没找到链顶,退出即可,上面说过了这样的修改不会影响任何人线段树上的值。
如果找到了链顶,就跳到上一条重链的链底,暴力找他3个孩子看他此时受不受影响,同时修改他线段树上的值。如果受影响,重复上面过程。
3叉树的意义在于跳重链时只用暴力查3个值,不破坏复杂度。
看别人题解线段树似乎是维护区间内孩子三元组是(0,0,1),(0,1,1)的信息,取反相当于(0,0,1)变(0,1,1),(0,1,1)变(0,0,1)。。