1. 程式人生 > 其它 >【洛谷P4332】三叉神經樹

【洛谷P4332】三叉神經樹

題目

題目連結:https://www.luogu.com.cn/problem/P4332
計算神經學作為新興的交叉學科近些年來一直是學術界的熱點。一種叫做SHOI 的神經組織因為其和近日發現的化合物 SHTSC 的密切聯絡引起了人們的極大關注。
SHOI 組織由若干個 SHOI 細胞構成,SHOI 細胞之間形成嚴密的樹形結構。每個 SHOI 細胞都有且只有一個輸出端,被稱為軸突,除了一個特殊的、被稱為根細胞的 SHOI 細胞的輸出作為整個組織的輸出以外,其餘細胞的軸突均連向其上級 SHOI 細胞;並且有且只有三個接收端,被稱為樹突,從其下級細胞或者其它神經組織那裡接收資訊。SHOI 細胞的訊號機制較為簡單,僅有 \(0\)

\(1\) 兩種。每個 SHOI 細胞根據三個輸入端中 \(0\)\(1\) 訊號的多寡輸出較多的那一種。
現在給出了一段 SHOI 組織的資訊,以及外部神經組織的輸入變化情況。請你模擬 SHOI 組織的輸出結果。
\(n,q\leq 5\times 10^5\)

思路

我們記 \(a_i\) 表示節點 \(x\) 的三個兒子中,\(1\) 的出現次數。下文點權也指代 \(a\)
對於一次修改,不妨設這次修改是把一個葉子從 \(0\) 改為 \(1\)。我們找到這個葉子的父親 \(x\),不難發現,需要修改的部分就是從 \(x\) 一直往上,且點權均為 \(1\) 的一段,再加上這一段上面的第一個節點。我們需要把這一段全部 \(+1\)


把葉子從 \(1\) 修改為 \(0\) 同理,只需要找到一段全為 \(2\) 的。
考慮用 LCT 維護這個過程。對於每一棵 Splay,維護其中深度最深的,且點權不為 \(1/2\) 的點的編號。對於一次修改操作,假設為從 \(0\) 改為 \(1\),我們就直接把 \(x\) access 後 splay 到根,查詢當前這棵 Splay 的深度最大的不為 \(1\) 的點,把這個點旋到根後,只需要把這個點和他的右子樹全部加一即可。
因為如果我們需要把一個區間加一,那麼這個區間肯定是全為 \(1\) 的,所以可以直接把最深的點權不為 \(1/2\) 的點的編號交換一下。
需要注意的是,如果整條鏈的點權都是 \(1\)
,那麼只需要在根節點上直接修改即可。
時間複雜度 \(O(Q\log n)\)

程式碼

#include <bits/stdc++.h>
using namespace std;

const int N=500010;
int n,Q,fat[N*3],a[N*3],son[N][3];

int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

struct LCT
{
	int val[N],fa[N],id[N][2],ch[N][2];
	bool lazy[N];
	
	bool pos(int x) { return x==ch[fa[x]][1]; }
	bool notrt(int x) { return x==ch[fa[x]][0] || x==ch[fa[x]][1]; }
	
	void pushup(int x)
	{
		int lc=ch[x][0],rc=ch[x][1];
		id[x][0]=id[rc][0] ? id[rc][0] : (val[x]==1 ? id[lc][0] : x);
		id[x][1]=id[rc][1] ? id[rc][1] : (val[x]==2 ? id[lc][1] : x);
	}
	
	void update(int x)
	{
		val[x]^=3; lazy[x]^=1;
		swap(id[x][0],id[x][1]);
	}
	
	void pushdown(int x)
	{
		if (lazy[x])
		{
			if (ch[x][0]) update(ch[x][0]);
			if (ch[x][1]) update(ch[x][1]);
			lazy[x]=0;
		}
	}
	
	void rotate(int x)
	{
		int y=fa[x],z=fa[y],k=pos(x),c=ch[x][k^1];
		if (notrt(y)) ch[z][pos(y)]=x; ch[x][k^1]=y; ch[y][k]=c;
		if (c) fa[c]=y; fa[y]=x; fa[x]=z;
		pushup(y); pushup(x);
	}
	
	void splay(int x)
	{
		stack<int> st; st.push(x);
		for (int i=x;notrt(i);i=fa[i]) st.push(fa[i]);
		for (;st.size();st.pop()) pushdown(st.top());
		for (;notrt(x);rotate(x))
		{
			int y=fa[x];
			if (notrt(y)) rotate(pos(x)==pos(y) ? y : x);
		} 
	}
	
	void access(int x)
	{
		for (int y=0;x;y=x,x=fa[x])
		{
			splay(x); ch[x][1]=y;
			pushup(x);
		}
	}
}lct;

int dfs(int x)
{
	if (x>n) return a[x];
	a[x]=dfs(son[x][0])+dfs(son[x][1])+dfs(son[x][2]);
	lct.fa[x]=fat[x]; lct.val[x]=a[x];
	lct.pushup(x); a[x]=(a[x]>=2);
	return a[x];
}

int main()
{
	n=read();
	for (int i=1;i<=n;i++)
		for (int j=0;j<=2;j++) 
			son[i][j]=read(),fat[son[i][j]]=i;
	for (int i=n+1;i<=3*n+1;i++) a[i]=read();
	dfs(1);
	Q=read();
	while (Q--)
	{
		int x=read(); a[x]^=1;
		int v=a[x]; x=fat[x];
		lct.access(x); lct.splay(x);
		if (v)
		{
			if (!lct.id[x][0]) lct.update(x);
			else
			{
				x=lct.id[x][0];
				lct.splay(x); lct.val[x]++;
				if (lct.ch[x][1]) lct.update(lct.ch[x][1]);
				lct.pushup(x);
			}
		}
		else
		{
			if (!lct.id[x][1]) lct.update(x);
			else
			{
				x=lct.id[x][1];
				lct.splay(x); lct.val[x]--;
				if (lct.ch[x][1]) lct.update(lct.ch[x][1]);
				lct.pushup(x);
			}
		}
		lct.splay(1);
		cout<<(lct.val[1]>=2)<<"\n";
	}
	return 0;
}