1. 程式人生 > 實用技巧 >「WC2020T1」有根樹

「WC2020T1」有根樹

「WC2020T1」有根樹


source:LOJ3329

  • 記與根節點在同一個聯通塊內的點的個數為 \(cnt\),與根節點不在同一個聯通塊內的點的 \(w\) 的最大值為 \(val\)

  • 如何滿足 \(max\{cnt,val\}\) 最小的要求?

  • 容易發現,可以通過往與根節點所在的連通塊刪除或增加一個節點,使得\(cnt\)增加或者減少\(1\)

  • 所以,\(cnt\) 是可以控制的,而\(val\)是難以實現\(1\)範圍內的變化

  • 我們要求 \(cnt\geq val\),找到最小的滿足條件的 \(cnt\),即為答案

  • 如何維護動態的增加和刪除節點?

  • 對於動態的增加與刪除節點,答案最多隻會變化\(1\)

  • 對於當前的樹的每一條葉子節點到根節點的路徑上,存在兩個相鄰節點(可能有一方為空),一個節點和根節點在同一個聯通塊內而另一個不在,維護這兩個邊界節點就可以快速地維護答案的變化

  • 如何維護邊界節點?

  • 樹剖,維護一條重鏈上的邊界節點(注意判斷重鏈上沒有邊界節點的情況),用樹狀陣列維護每個節點的答案

    在刪除節點的時候 \(set\) 等查詢前驅後繼更新

    用連結串列或者 \(set\) 維護與根節點不在一個連通塊的點的\(w\)最大值

    記錄與根節點在同一個連通塊的節點個數,判斷大小並更新答案

  • 線段樹,維護與根節點不在同一個連通塊內的\(w\)的最大值,維護上述過程

「solution x00 」樹剖|樹狀陣列|set (90pnts)

  #include<bits/stdc++.h>
  using namespace std;
  const int Max_N=5e5,Max_Q=1e6;
  int n,q,num=0,Num=0,ans=0,A[Max_N+10]={},bln[Max_N+10]={},lin[Max_N+10]={},dfn[Max_N+10]={},size[Max_N+10]={},son[Max_N+10]={},fa[Max_N+10]={},Top[Max_N+10]={},v[Max_N]={},V[Max_N+10]={},Tag[Max_N+10]={};
  set<int> a[Max_N+10];
  set< pair<int,int> > X,Y;
  struct kk{
  	int Id,Next;
  } e[Max_N*2+10]={};
  inline void insert(int x,int y){
  	e[++Num].Next=lin[x]; lin[x]=Num; e[Num].Id=y;
  }
  inline void dfs(int x){
  	size[x]=1;
  	for(int i=lin[x];i;i=e[i].Next){
  		if(e[i].Id==fa[x]) continue;
  		fa[e[i].Id]=x;
  		dfs(e[i].Id);
  		size[x]+=size[e[i].Id];
  		if(size[e[i].Id]>size[son[x]]) son[x]=e[i].Id;
  	}
  }
  inline void Dfs(int x,int TOP){
  	bln[dfn[x]=++num]=x; Top[x]=TOP;
  	if(son[x]) Dfs(son[x],TOP);
  	for(int i=lin[x];i;i=e[i].Next){
  		if(e[i].Id==fa[x]||e[i].Id==son[x]) continue;
  		Dfs(e[i].Id,e[i].Id);
  	}
  }
  inline void Add(int x,int k){
  	for(;x<=n;x+=x&-x) A[x]+=k;
  }
  inline int Ask(int x){
  	int sum=0;
  	for(;x;x-=x&-x) sum+=A[x];
  	return sum;
  }
  inline void Insert(int x){
  	int val=Ask(dfn[x]);
  	if(val>ans){
  		++ans; Tag[x]=1;
  		if(!v[Top[x]]) X.insert(make_pair(val,v[Top[x]]=dfn[x]));
  		else if(dfn[x]>v[Top[x]]){
  			X.erase(make_pair(Ask(v[Top[x]]),v[Top[x]]));
  			X.insert(make_pair(val,v[Top[x]]=dfn[x]));
  		}
  	} else {
  		if(!V[Top[x]]) Y.insert(make_pair(val,V[Top[x]]=dfn[x]));
  		else if(dfn[x]<V[Top[x]]){
  			Y.erase(make_pair(Ask(V[Top[x]]),V[Top[x]]));
  			Y.insert(make_pair(val,V[Top[x]]=dfn[x]));
  		}
  	}
  }
  inline void Erase(int x){
  	int val=Ask(dfn[x]);
  	set<int>::iterator it=a[Top[x]].lower_bound(dfn[x]);
  	ans-=Tag[x]; Tag[x]=0;
  	if(v[Top[x]]==dfn[x]){
  		int New=*(--it);
  		X.erase(make_pair(val,dfn[x]));
  		if(New==0) v[Top[x]]=0;
  		else v[Top[x]]=New;
  		if(v[Top[x]]) X.insert(make_pair(Ask(v[Top[x]]),v[Top[x]]));
  	}
  	if(V[Top[x]]==dfn[x]){
  		if(dfn[x]==*(it)) ++it;
  		int New=*(it);
  		Y.erase(make_pair(val,dfn[x]));
  		if(New==n+1) V[Top[x]]=0;
  		else V[Top[x]]=New;
  		if(V[Top[x]]) Y.insert(make_pair(Ask(V[Top[x]]),V[Top[x]]));
  	}
  }
  inline void ADD(int x,int k){
  	if(k==1) a[Top[x]].insert(dfn[x]),Insert(x);
  	else Erase(x),a[Top[x]].erase(dfn[x]);
  	for(;x;x=fa[Top[x]]){
  		if(v[Top[x]]<=dfn[x]&&v[Top[x]]){
  			int val=Ask(v[Top[x]]);
  			X.erase(make_pair(val,v[Top[x]])); 
  			X.insert(make_pair(val+k,v[Top[x]]));
  		} 
  		if(V[Top[x]]<=dfn[x]&&V[Top[x]]){
  			int val=Ask(V[Top[x]]);
  			Y.erase(make_pair(val,V[Top[x]])); 
  			Y.insert(make_pair(val+k,V[Top[x]]));
  		}
  		Add(dfn[Top[x]],k);
  		Add(dfn[x]+1,-k);
  	}//在維護的時候沒有進行答案維護,可能出現了錯誤
  	if(X.size()&&Y.size()){
  		set< pair<int,int> >::iterator it=X.begin(),It=Y.end(); --It;
  		pair<int,int> itt=*it,Itt=*It;
  		if(itt.first<Itt.first){
  			a[Top[bln[itt.second]]].erase(itt.second);
  			a[Top[bln[Itt.second]]].erase(Itt.second);
  			Erase(bln[itt.second]);
  			Erase(bln[Itt.second]);
  			Insert(bln[Itt.second]); 
  			Insert(bln[itt.second]);
  			a[Top[bln[itt.second]]].insert(itt.second);
  			a[Top[bln[Itt.second]]].insert(Itt.second);
  		}
  	}
  	if(X.size()){
  		set< pair<int,int> >::iterator it=X.begin();
  		pair<int,int> itt=*it;
  		if(ans>itt.first) Erase(bln[itt.second]),Insert(bln[itt.second]);
  	}
  	if(Y.size()){
  		set< pair<int,int> >::iterator it=Y.end(); --it;
  		pair<int,int> itt=*it;
  		if(ans<itt.first) Erase(bln[itt.second]),Insert(bln[itt.second]);
  	}
  }
  int main(){
  	scanf("%d",&n);
  	for(int i=1;i<n;i++){
  		int x,y; scanf("%d%d",&x,&y);
  		insert(x,y); insert(y,x);
  	}
  	dfs(1); Dfs(1,1);
  	for(int i=1;i<=n;i++) a[i].insert(0),a[i].insert(n+1); 
  	scanf("%d",&q);
  	for(int i=1;i<=q;i++){
  		int opt,x; scanf("%d%d",&opt,&x);
  		if(opt==1) ADD(x,1);
  		if(opt==2) ADD(x,-1);
  		printf("%d\n",ans);
  	}
  	return 0;
  }

「solution x10 」樹剖|樹狀陣列|連結串列

  • 對於增加/刪除對其他點的影響:

    樹剖,每次修改 \([dfn[Top[x]],dfn[x]]\)

    更改:樹剖+樹狀陣列 \(O(log_2^2n)\) + 詢問:樹狀陣列 \(O(log_2n)\)

    每次只需要標記一下當前增加/刪除的點,用 \(dfs\) 序記錄子樹的區間,區間求和詢問當前點的值

    更改:樹狀陣列 \(O(log_2n)\) + 詢問:樹狀陣列 \(O(log_2n)\)

  • 對於一條路徑上只會進行最多兩個次更新(一條路徑上只會有兩個邊界點),時間複雜度\(O(log_2n)\)

  • 記錄根節點所在的聯通塊的點的個數\(cnt\),與和根節點不在同一個聯通塊的點的權值的最大值\(lim\)

    用連結串列\(lup\)記錄與根節點在同一個連通塊內的邊界上的點,\(lim\) 應當比塊內的最大權值小

    用連結串列\(ldw\)記錄與根節點不在同一個連通塊的邊界上的點,\(lim\) 應當不小於塊內的最大權值

    相對於 \(set\) 查詢,常數較小

  • 樹狀陣列維護當前這條重鏈 \(x\) 的前一個標記點和後一個標記點,樹狀陣列上二分查詢

    相對於 \(set\) ,常數較小

  • 對於增加/刪除需要與當前重鏈的兩個邊界點進行判斷,實現細節較多

  • 時間複雜度:\(O(qlog_2n)\)

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> inline void chkmin(T &a, const T &b) { a = a < b ? a : b; }
template<typename T> inline void chkmax(T &a, const T &b) { a = a > b ? a : b; }

namespace IO {
	const int MAXR = 10000000;
	char rbuf[MAXR], pbuf[MAXR];
	int rpos, ppos, rlen;
	inline char readc() {
	#ifndef ONLINE_JUDGE
		return getchar();
	#endif
		if (!rpos) {
			if (feof(stdin)) return -1;
			rlen = fread(rbuf, 1, MAXR, stdin);
		}
		char c = rbuf[rpos++];
		if (rpos == rlen) rpos = 0;
		return c;
	}
	template<typename T> inline int read(T &x) {
		x = 0; register int flag = 1, c;
		while (((c = readc()) < '0' || c > '9') && c != '-')
			if (c < 0) return -1;
		if (c == '-') flag = -1; else x = c - '0';
		while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
		x *= flag; return 0;
	}
	template<typename T1, typename ...T2> inline int read(T1 &a, T2&... x) {
		return read(a) | read(x...);
	}
	inline void ioflush() { fwrite(pbuf, 1, ppos, stdout), ppos = 0; fflush(stdout); }
	inline void printc(char c) {
		if (!c) return;
		pbuf[ppos++] = c;
		if (ppos == MAXR) ioflush();
	}
	template<typename T> inline void print(T x, char c = '\n') {
		if (x < 0) printc('-'), x = -x;
		if (x) {
			static char sta[20];
			register int tp = 0;
			for (; x; x /= 10) sta[tp++] = x % 10 + '0';
			while (tp > 0) printc(sta[--tp]);
		} else printc('0');
		if (c) printc(c);
	}
}

const int MAXN = 500005;
struct Edge { int to, next; } edge[MAXN << 1];
int par[MAXN], up[MAXN], vup[MAXN], dw[MAXN], vdw[MAXN];
int dfn[MAXN], top[MAXN], lg[MAXN], head[MAXN], sz[MAXN];
int wson[MAXN], mem[MAXN], num[MAXN], ed[MAXN], bit[MAXN];
int pla[MAXN], n, tot, lim, cnt, Q;

inline void add_edge(int u, int v) {
	edge[++tot] = Edge { v, head[u] };
	head[u] = tot;
}

void dfs1(int u, int fa) {
	sz[u] = 1, par[u] = fa;
	for (int i = head[u]; i; i = edge[i].next) {
		int v = edge[i].to;
		if (v == fa) continue;
		dfs1(v, u);
		if (sz[v] > sz[wson[u]]) wson[u] = v;
		sz[u] += sz[v];
	}
}

void dfs2(int u, int fa, int t) {
	++num[t], pla[dfn[u] = ++tot] = u, top[u] = t;
	if (wson[u]) dfs2(wson[u], u, t);
	for (int i = head[u]; i; i = edge[i].next) {
		int v = edge[i].to;
		if (v != fa && v != wson[u]) dfs2(v, u, v);
	}
	ed[u] = tot;
}

struct Link {
	int pre[MAXN << 1], nxt[MAXN << 1];
	inline void del(int x) {
		pre[nxt[x]] = pre[x];
		nxt[pre[x]] = nxt[x];
		nxt[x] = pre[x] = x;
	}
	inline void ins(int h, int x) {
		h += n + 1;//h代表x節點的w,對於每一種價值維護一個連結串列,記錄所有的價值為h的點的編號
		nxt[x] = nxt[h];
		pre[nxt[h]] = x;
		nxt[h] = x;
		pre[x] = h;
	}
	inline int get(int h) {
		return nxt[h + n + 1];
	}
} lup, ldw;

inline void update(int x) {
	lup.del(x);
	if (up[x]) lup.ins(vup[x], x);
	ldw.del(x);
	if (dw[x] <= num[x]) ldw.ins(vdw[x], x);
}//更新

inline void add(int *bit, int n, int x, int w) {
	for (; x <= n; x += x & -x) bit[x] += w;
}

inline int pred(const int *bit, int n, int x) {
	for (--x; x && !bit[x]; x -= x & -x);
	if (!x) return 0;
	int y = x - (x & -x), s = 0;
	for (int i = (x & -x) >> 1; i; i >>= 1)
		if (s + bit[y | i] < bit[x]) s += bit[y |= i];
	return y + 1;
}//查詢前驅

inline int succ(const int *bit, int n, int x) {
	int s = 0;
	for (; x; x -= x & -x) s += bit[x];
	for (int i = lg[n]; i >= 0; i--)
		if ((x | 1 << i) <= n && bit[x | 1 << i] <= s) s -= bit[x |= 1 << i];
	return x + 1;
}//查詢後繼

inline int ask(const int *bit, int n, int x) {
	int res = 0;
	for (; x; x -= x & -x) res += bit[x];
	return res;
}

inline int get_w(int x) {
	return ask(bit, n, ed[x]) - ask(bit, n, dfn[x] - 1);
}

inline void erase_up(int x, bool f = true) {
	if (f) dw[x] = up[x], vdw[x] = vup[x];
	up[x] = pred(mem + dfn[x] - 1, num[x], up[x]);
	vup[x] = !up[x] ? 0 : get_w(pla[dfn[x] + up[x] - 1]);
}

inline void erase_dw(int x, bool f = true) {
	if (f) up[x] = dw[x], vup[x] = vdw[x];
	dw[x] = succ(mem + dfn[x] - 1, num[x], dw[x]);
	vdw[x] = dw[x] > num[x] ? 0 : get_w(pla[dfn[x] + dw[x] - 1]);
}

inline void update_val(int x, int d, int w) {
	if (up[x] && d >= up[x]) vup[x] += w;
	if (d >= dw[x]) vdw[x] += w;
}//更新關鍵節點

void insert(int x) {
	add(bit, n, dfn[x], 1);
	int t = top[x], d = dfn[x] - dfn[t] + 1;
	add(mem + dfn[t] - 1, num[t], d, 1);
	update_val(t, d, 1);
	if (vdw[t] > lim) {
		++cnt;
		erase_dw(t);
	} else if (d < dw[t]) {
		int s = get_w(x);
		if (s <= lim) {
			dw[t] = d;
			vdw[t] = s;
		} else {
			++cnt;
			if (d > up[t]) {
				up[t] = d;
				vup[t] = s;
			}
		}
	}
	update(t);
	
	while ((x = par[t]) && (t = top[x])) {
		update_val(t, d = dfn[x] - dfn[t] + 1, 1);
		if (vdw[t] > lim) {
			++cnt;
			erase_dw(t);
		}
		update(t);
	}
	
	while (cnt > lim) {
		int g = lup.get(lim);
		if (g > n) { ++lim; continue; }
		--cnt;
		erase_up(g);
		update(g);
	}
}

void remove(int x) {
	add(bit, n, dfn[x], -1);
	int t = top[x], d = dfn[x] - dfn[t] + 1;
	add(mem + dfn[t] - 1, num[t], d, -1);
	update_val(t, d, -1);
	if (d <= up[t]) --cnt;
	if (d == up[t]) erase_up(t, false);
	else if (d == dw[t]) erase_dw(t, false);
	if (up[t] && vup[t] <= lim) {
		erase_up(t);
		--cnt;
	}
	update(t);
	
	while ((x = par[t]) && (t = top[x])) {
		update_val(t, d = dfn[x] - dfn[t] + 1, -1);
		if (up[t] && vup[t] <= lim) {
			erase_up(t);
			--cnt;
		}
		update(t);
	}
	
	while (cnt < lim) {
		int g = ldw.get(lim);
		if (g > n) { --lim; continue; }
		++cnt;
		erase_dw(g);
		update(g);
	}
}

int main() {
	IO::read(n);
	for (int i = 1; i < n; i++) {
		int u, v; IO::read(u, v);
		add_edge(u, v), add_edge(v, u);
	}
	dfs1(1, 0);
	dfs2(1, tot = 0, 1);
	for (int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
	for (int i = 1; i <= n * 2; i++) {
		lup.pre[i] = lup.nxt[i] = i;
		ldw.pre[i] = ldw.nxt[i] = i;
	}
	for (int i = 1; i <= n; i++)
		if (top[i] == i) dw[i] = num[i] + 1;
	IO::read(Q);
	while (Q--) {
		int t, p; IO::read(t, p);
		if (t == 1) insert(p);
		else remove(p);
		IO::print(cnt);
	}
	IO::ioflush();
	return 0;
}

「solution x20 」樹剖|線段樹(丁曉漫)

  • 實現更為簡單
  • 時間複雜度:\(O(nlog^2_2n)\)
#include<bits/stdc++.h>
using namespace std;

namespace io{
	const int size=1<<22|1;
	char iBuf[size],*iS,*iT,c;
	char oBuf[size],*oS=oBuf,*oT=oBuf+size;
	char getc(){
		if(iS==iT){
			iS=iBuf;
			iT=iBuf+fread(iBuf,1,size,stdin);
		}
		if(iS==iT)return EOF;
		return *iS++;
	}
	template<class T>void qread(T &x){
		int f=1;
		for(c=getc();c<'0'||c>'9';c=getc())
			if(c=='-')f=-1;
		for(x=0;c>='0'&&c<='9';c=getc())
			x=(x<<3)+(x<<1)+(c&15);
		x*=f;
	}
	void flush(){
		fwrite(oBuf,1,oS-oBuf,stdout);
		oS=oBuf; 
	}
	void putc(char x){
		*oS++=x;
		if(oS==oT)flush();
	}
	template<class T>void qwrite(T x){
		if(x<0)putc('-'),x=-x;
		static char qu[55];
		char *tmp=qu;
		do *tmp++=(x%10)^'0';while(x/=10);
		while(tmp--!=qu)putc(*tmp);
	}
	struct flusher{
		~flusher(){flush();}
	}_;
}

const int maxn=500005;
const int INF=0x3f3f3f3f;
int n,k,q,ans;
vector<int>edge[maxn];
int sz[maxn],son[maxn],par[maxn],top[maxn],dfn[maxn],rev[maxn];
bool in[maxn];

namespace Segtree{
	pair<int,int>mn[maxn*4],mx[maxn*4];
	int tag[maxn*4];
	void pushup(int cur){
		mn[cur]=min(mn[cur<<1],mn[cur<<1|1]);
		mx[cur]=max(mx[cur<<1],mx[cur<<1|1]);
		mn[cur].first+=tag[cur];
		mx[cur].first+=tag[cur];
	}
	void build(int cur,int l,int r){
		if(l==r){
			mn[cur]=make_pair(INF,rev[l]);
			mx[cur]=make_pair(-INF,rev[l]);
			return;
		}
		int mid=l+r>>1;
		build(cur<<1,l,mid);
		build(cur<<1|1,mid+1,r);
		pushup(cur);
	}
	void set(int cur,int l,int r,int k,int opmn,int opmx){
		if(l==r){
			mn[cur]=make_pair((opmn==0?INF:0)+tag[cur],rev[l]);
			mx[cur]=make_pair((opmx==0?-INF:0)+tag[cur],rev[l]);
			return;
		}
		int mid=l+r>>1;
		if(k<=mid)set(cur<<1,l,mid,k,opmn,opmx);
		else set(cur<<1|1,mid+1,r,k,opmn,opmx);
		pushup(cur);
	}//更改節點的所屬連通塊 
	void update(int cur,int l,int r,int vl,int vr,int val){
		if(l>=vl&&r<=vr){
			mn[cur].first+=val;
			mx[cur].first+=val;
			tag[cur]+=val;
			return;
		}
		int mid=l+r>>1;
		if(mid>=vl)update(cur<<1,l,mid,vl,vr,val);
		if(mid<vr)update(cur<<1|1,mid+1,r,vl,vr,val);
		pushup(cur);
	}//更新價值 
}

void dfs1(int x,int p){
	par[x]=p;
	sz[x]=1;
	for(int i=0;i<int(edge[x].size());i++){
		int y=edge[x][i];
		if(y==p)continue;
		dfs1(y,x);
		sz[x]+=sz[y];
		if(!son[x]||sz[y]>sz[son[x]])son[x]=y;
	}
}
void dfs2(int x,int p,int t){
	top[x]=t;
	dfn[x]=++k;
	rev[k]=x;
	if(son[x])dfs2(son[x],x,t);
	for(int i=0;i<int(edge[x].size());i++){
		int y=edge[x][i];
		if(y==p||y==son[x])continue;
		dfs2(y,x,y);
	}
}
void work(int x,int f){
	while(x){
		Segtree::update(1,1,n,dfn[top[x]],dfn[x],f);
		x=par[top[x]];
	}
}

int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	io::qread(n);
	for(int i=1;i<=n-1;i++){
		int u,v;
		io::qread(u);io::qread(v);
		edge[u].push_back(v);
		edge[v].push_back(u);
	}
	dfs1(1,0);
	dfs2(1,0,1);
	Segtree::build(1,1,n);
	io::qread(q);
	while(q--){
		int f,x;
		io::qread(f);io::qread(x);
		int nans=ans;
		if(f==1){
			work(x,1);
			Segtree::set(1,1,n,dfn[x],0,1);
			if(Segtree::mx[1].first>=ans+1){
				int x=Segtree::mx[1].second;
				in[x]=true;
				nans++;
				Segtree::set(1,1,n,dfn[x],1,0);
			}
			if(nans>ans&&Segtree::mn[1].first<=ans){
				int x=Segtree::mn[1].second;
				in[x]=false;
				nans--;
				Segtree::set(1,1,n,dfn[x],0,1);
			} //尋找合法的答案 
		}
		else{
			work(x,-1);
			nans-=in[x];
			in[x]=false;
			Segtree::set(1,1,n,dfn[x],0,0);
			if(Segtree::mn[1].first<=ans-1){
				int x=Segtree::mn[1].second;
				in[x]=false;
				nans--;
				Segtree::set(1,1,n,dfn[x],0,1);
			}
			if(nans<ans&&Segtree::mx[1].first>=ans){
				int x=Segtree::mx[1].second;
				in[x]=true;
				nans++;
				Segtree::set(1,1,n,dfn[x],1,0);
			}
		}
		ans=nans;
		io::qwrite(ans);io::putc('\n');
	}
	return 0;
}