[bzoj3553] [luogu4332] [SHOI2014] 三叉神經樹 - lct - 動態dp

傳送門:https://www.luogu.org/problemnew/show/P4332

題目大意:給定一棵樹,每個點有3個輸入信號的接口(連向外部或兒子)和1個輸出信號的接口(連向父親),1號點是根。信號是0或1。一個點輸出的信號是輸入信號中較多的那個。每次修改一個外部接口傳入的信號,輸出1號節點輸出的信息。

看到網上很多log^2甚至log^3的做法,這裏放一個lct的一個log做法,可能是我的原創做法吧畢竟網上目前似乎沒有找到類似思路的題解。

其實這題我的第一感覺是動態dp……說實話這確實像個動態dp模型,我們也可以用類似動態dp的做法來做。

首先,當一個點接收到>=2個1時,輸出1,否則輸出0。

我們可以在lct中維護一個si,表示它的虛兒子有多少個1傳上來。

再維護一個b(i,0/1),表示:i所在的這條重鏈,如果從鏈底額外輸入一個0/1,從鏈頂輸出的會是什麼值。

這是我的做法最大的特點:並非直接維護每個點具體會輸出什麼,而是維護一整段鏈的信息。(這裏是不是很像動態dp?)

爲什麼要記一個0/1呢?爲了方便合併重鏈的信息,只要考慮一個0/1的信息從右側傳進來,傳到中點時會變成什麼,再傳到左側去就行了。

access時只需要用當前點的b(i,0)值修改父親的s值,修改和查詢也都很好操作。

我們會發現這棵lct甚至不需要makeroot,所以無論是代碼難度還是運行速度都完爆樹剖!

#include<bits/stdc++.h>
using namespace std;
#define gc getchar()
#define pc putchar
#define li long long
inline int read(){
	int x = 0,c = gc;
	while(!isdigit(c)) c = gc;
	while(isdigit(c)) x = (x << 1) + (x << 3) + (c ^ '0'),c = gc;
	return x;
}
inline void print(int q){
	if(q >= 10) print(q / 10);
	pc(q % 10 + '0');
}
int n,m,f[2000010],fsts[2000010],nxt[2000010],l[2000010],r[2000010],s[2000010];
bool a[2000010],b[2000010][2];
void dfs(int q){
	for(int i = fsts[q];i;i = nxt[i]){
		if(i <= n) dfs(i);
		s[q] += a[i];
	}
	a[q] = b[q][0] = (s[q] >= 2);
	b[q][1] = (s[q] >= 1);
}
inline bool is(int q){
	return l[f[q]] != q && r[f[q]] != q;
}
inline void ud(int q){
	b[q][0] = (s[q] >= 2);
	b[q][1] = (s[q] >= 1);
	if(r[q]){
		if(b[r[q]][0]) b[q][0] = b[q][1];
		else if(!b[r[q]][1]) b[q][1] = b[q][0];
	}
	if(l[q]){
		if(b[q][0]) b[q][0] = b[q][1] = b[l[q]][1];
		else if(!b[q][1]) b[q][0] = b[q][1] = b[l[q]][0];
		else b[q][0] = b[l[q]][0],b[q][1] = b[l[q]][1];
	}
}
inline void ro(int q){
	int p = f[q];
	if(l[f[p]] == p) l[f[p]] = q;
	else if(r[f[p]] == p) r[f[p]] = q;
	f[q] = f[p];f[p] = q;
	if(l[p] == q){
		l[p] = r[q];r[q] = p;
		if(l[p]) f[l[p]] = p;
	}
	else{
		r[p] = l[q];l[q] = p;
		if(r[p]) f[r[p]] = p;
	}
	b[q][0] = b[p][0];b[q][1] = b[p][1];ud(p);
}
inline void sp(int q){
	while(!is(q)){
		int p = f[q];
		if(!is(p)){
			if((l[f[p]] == p) ^ (l[p] == q)) ro(q);
			else ro(p);
		}
		ro(q);
	}
}
inline void ac(int q){
	int p = 0;
	while(q){
		sp(q);
		s[q] += b[r[q]][0];
		r[q] = p;
		s[q] -= b[p][0];
		ud(q);
		p = q;q = f[q];
	}
}
int main(){
	int i,u;
	n = read();
	for(i = 1;i <= n;++i){
		u = read();f[u] = i;nxt[u] = fsts[i];fsts[i] = u;
		u = read();f[u] = i;nxt[u] = fsts[i];fsts[i] = u;
		u = read();f[u] = i;nxt[u] = fsts[i];fsts[i] = u;
	}
	for(i = n + 1;i <= 3 * n + 1;++i) a[i] = read();
	dfs(1);
	m = read();
	for(i = 1;i <= m;++i){
		u = read();
		ac(f[u]);sp(f[u]);
		a[u] ^= 1;a[u] ? ++s[f[u]] : --s[f[u]];
		ud(u);
		ac(1);sp(1);print(b[1][0]);pc('\n');
	}
	return 0;
}

目前在luogu上2411ms是rk1。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章