洛谷4322 SHOI2014 三叉神經樹(LCT+思維)
阿新 • • 發佈:2018-12-04
好久之前做的題了QWQ
現在來補一發部落格
一道神仙題啊。。qwq
首先,我們可以看出來,我們如果對於每個點維護一個 ,表示他的直系兒子中有幾個表現為1的。
那麼 就是他反應的型別
這樣十分便於我們計算一開始的
那麼考慮修改。
一定是會修改一條
也就是說,如果我們能夠知道一次修改, 的路徑下最下面的1或者2的位置,我們就能夠通過鏈修改來實現。
其實一開始我想的是二分
我們發現,可以通過 維護最深的不是 的位置和不是 的位置
那麼我們對於一次修改,假設由
如果
,那麼說明整條鏈都會被修改,直接修改整條路徑
不然,我們就將路徑提出來,
之後,修改他的右兒子,表示他下面的點。
然後把當前點的
修改,但是不改變別的量。
QWQ有一些細節,對於修改的時候,由於路徑上的
都是1或者2。
所以修改的之後可以直接
具體細節看程式碼實現吧
裡面有詳細的註釋
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2e6+1e2;
int ch[maxn][3];
int fa[maxn],val[maxn];
int tag[maxn];
int n,m;
int num1[maxn],num2[maxn]; //深度最深的 兒子數不為1 或者 2 的 節點是的編號
int st[maxn];
int son(int x)
{
if (ch[fa[x]][0]==x) return 0;
else return 1;
}
bool notroot(int x)
{
return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void update(int x) //由於是深度最深,我們一定是先考慮右子樹,再說當前點,再是右子樹
{
num1[x]=num1[ch[x][1]];
if (!num1[x] && val[x]!=1) num1[x]=x;
if (!num1[x]) num1[x]=num1[ch[x][0]];
num2[x]=num2[ch[x][1]];
if (!num2[x] && val[x]!=2) num2[x]=x;
if (!num2[x]) num2[x]=num2[ch[x][0]];
}
void solve(int x,int d)
{
val[x]^=3;
swap(num1[x],num2[x]); //修改的時候,必定是一段全為1或者2的區間,所以一個一定是0,直接交換是沒錯的
tag[x]+=d;
}
void pushdown(int x)
{
if (tag[x])
{
if (ch[x][0]) solve(ch[x][0],tag[x]);
if (ch[x][1]) solve(ch[x][1],tag[x]);
tag[x]=0;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y];
int b=son(x),c=son(y);
if (notroot(y)) ch[z][c]=x;
fa[x]=z;
ch[y][b]=ch[x][!b];
fa[ch[x][!b]]=y;
ch[x][!b]=y;
fa[y]=x;
update(y);
update(x);
}
void splay(int x)
{
int y=x,cnt=0;
st[++cnt]=y;
while (notroot(y)) y=fa[y],st[++cnt]=y;
while (cnt) pushdown(st[cnt--]);
while (notroot(x))
{
int y=fa[x],z=fa[y];
int b=son(x),c=son(y);
if (notroot(y))
{
if (b==c) rotate(y);
else rotate(x);
}
rotate(x);
}
update(x);
}
void access(int x)
{
for (int y=0;x;y=x,x=fa[x])
{
splay(x);
ch[x][1]=y;
update(x);
}
}
int in[maxn];
queue<int> q;
int main()
{
n=read();
for (int i=1;i<=n;i++)
{
int x=read(),y=read(),w=read();
in[i]=3;
fa[x]=fa[y]=fa[w]=i;
}
for (int i=n+1;i<=3*n+1;i++) val[i]=read()*2,q.push(i); //我們事先val都*2,那麼對於每個點,他表現出來的特徵就是val>>1
int m=read();
while (!q.empty()) //拓撲排序先預處理出來每個點的val
{
int x=q.front();
q.pop();
if (x<=n) update(x);
val[fa[x]]+=val[x]/2;
in[fa[x]]--;
if (!in[fa[x]]) q.push(fa[x]);
}
int ans=val[1]>>1;
//在本題中,val表示兒子的表示1的數量,那麼val>>1就相當於每個點表達的資訊
for (int i=1;i<=m;i++)
{
int x=read();
val[x]^=2; //葉子節點只有可能是0或者1,而乘2之後就是0或者2
int k = val[x] - 1;
x=fa[x]; //不修改底下的葉子節點
access(x);
splay(x); //打通這個點到1的路徑
int now;
if (k==-1) now = num2[x];
else now = num1[x];
if (!now)
{
solve(x,k);
update(x);
ans^=1;
}
else
{
splay(now);
solve(ch[now][1],k);
update(ch[now][1]);
val[now]+=k;
update(now);
}
cout<<ans<<"\n";
}
return 0;
}