6869. 【2020.11.17提高組模擬】鼠樹


一棵樹,點有黑白。稱某個點被最近的黑點祖先所“管轄”。管轄其它點的點稱為“管轄點”。

支持如下操作:

  1. 詢問\(x\)的權值。
  2. \(x\)所管轄的點加權值。
  3. 詢問\(x\)的子樹的權值和。
  4. \(x\)的子樹中所有的管轄點做操作。
  5. \(x\)變黑。
  6. \(x\)變白。

\(n\le 3*10^5,Q\le 5*10^5\)


比賽時為什么沒有搞出來……

為了方便記\(x\)的管轄點為\(y\)

由於修改操作太麻煩,不妨考慮操作2只在\(y\)處加權值,操作1找\(y\)的權值,操作3求\(x\)子樹中的管轄點的權值和乘管轄范圍,另外加上\(y\)的權值乘\(x\)子樹中包含\(x\)的白色聯通塊的大小,操作4區間改管轄點的權值。這樣在沒有5和6操作時可以隨便做。

現在支持5和6操作。5操作時,找出\(y\)的權值,插入新點,並將新點的權值設為\(y\)的權值;6操作時,刪除之后,給\(x\)的子樹整體加\(w_x-w_y\),再用4操作給\(x\)中所有管轄點權值加\(-(w_x-w_y)\)\(w\)為權值。

操作的時候有些小操作,這里就不說了。

具體的實現的時候,搞三個數據結構:

  1. 用樹剖+set,可以做到\(O(\lg n)\)地找到每個點對應的管轄點。
  2. 用樹狀數組,維護給子樹整體加的操作。
  3. 用線段樹,維護形如\(\sum c_iw_i\)的序列,支持查區間查\(\sum c_iw_i\),區間查\(\sum c_i\),單點查\(w_i\),單點改\(c_i\),區間改\(w_i\)(只改\(c_i>0\)的部分)。不知道樹狀數組行不行呢。

時間復雜度\(O(n\lg n)\)


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define N 300005
#define ui unsigned
#define INF 1000000000
int n,m;
int fa[N];
struct EDGE{
	int to;
	EDGE *las;
} e[N];
int ne;
EDGE *last[N];

int dep[N],siz[N],hs[N],top[N],in[N],out[N],nowdfn;
int mn[N];
struct cmp{bool operator()(int a,int b){return dep[a]<dep[b];}};
set<int,cmp> b[N];
void updmn(int x){
	mn[x]=(b[x].empty()?INF:dep[*b[x].begin()]);
}
void init1(int x){
	in[x]=++nowdfn;
	siz[x]=1;
	for (EDGE *ei=last[x];ei;ei=ei->las){
		dep[ei->to]=dep[x]+1;
		init1(ei->to);
		siz[x]+=siz[ei->to];
		if (siz[ei->to]>siz[hs[x]])
			hs[x]=ei->to;
	}
	out[x]=nowdfn;
}
void init2(int x,int t){
	top[x]=t;
	if (hs[x]){
		init2(hs[x],t);
		for (EDGE *ei=last[x];ei;ei=ei->las)
			if (ei->to!=hs[x])
				init2(ei->to,ei->to);
	}
}
int find(int x){
	for (;x;x=fa[top[x]])
		if (mn[top[x]]<=dep[x]){
			auto p=b[top[x]].upper_bound(x);
			--p;
			return *p;
		}
}

struct TA{
	ui t[N];
	void add(int x,ui c){
		for (;x<=n;x+=x&-x)
			t[x]+=c;
	}
	ui query(int x){
		ui r=0;
		for (;x;x-=x&-x)
			r+=t[x];
		return r;
	}
};
struct TA2{
	TA t0,t1;
	void add(int l,int r,ui c){
		t1.add(l,c),t1.add(r+1,-c);
		t0.add(l,-c*(l-1)),t0.add(r+1,c*r);
	}
	ui query(int l,int r){
		ui s=0;
		s+=t0.query(r)+t1.query(r)*(ui)r;
		s-=t0.query(l-1)+t1.query(l-1)*(ui)(l-1);
		return s;
	}
} s;

ui c[N*4],w[N*4],cw[N*4];
void gt(int k,ui v){
	if (c[k]){
		cw[k]+=c[k]*v;
		w[k]+=v;
	}
}
void upd(int k){
	c[k]=c[k<<1]+c[k<<1|1];
	cw[k]=cw[k<<1]+cw[k<<1|1];
}
void pd(int k){
	gt(k<<1,w[k]);
	gt(k<<1|1,w[k]);
	w[k]=0;
}
ui sum(ui q[],int st,int en,int k=1,int l=1,int r=n){
	if (st<=l && r<=en)
		return q[k];
	pd(k);
	ui res=0;
	int mid=l+r>>1;
	if (st<=mid) res+=sum(q,st,en,k<<1,l,mid);
	if (mid<en) res+=sum(q,st,en,k<<1|1,mid+1,r);
	return res;
}
void addc(int x,ui v,int k=1,int l=1,int r=n){
	if (l==r){
		c[k]+=v;
		if (!c[k]) w[k]=0;
		cw[k]=c[k]*w[k];
		return;
	}
	pd(k);
	int mid=l+r>>1;
	if (x<=mid) addc(x,v,k<<1,l,mid);
	else addc(x,v,k<<1|1,mid+1,r);
	upd(k);
}
void addw(int st,int en,ui v,int k=1,int l=1,int r=n){
	if (st<=l && r<=en){
		gt(k,v);
		return;
	}
	pd(k);
	int mid=l+r>>1;
	if (st<=mid) addw(st,en,v,k<<1,l,mid);
	if (mid<en) addw(st,en,v,k<<1|1,mid+1,r);
	upd(k);
}
ui qw(int x){return sum(w,in[x],in[x]);}
ui qc(int x){return sum(c,in[x],in[x]);}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	freopen("pastel.in","r",stdin);
	freopen("pastel.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=2;i<=n;++i){
		scanf("%d",&fa[i]);
		e[ne]={i,last[fa[i]]};
		last[fa[i]]=e+ne++;
	}
	init1(1);
	init2(1,1);
	for (int i=1;i<=n;++i)
		mn[i]=INF;
	b[1].insert(1);
	updmn(1);
	addc(in[1],siz[1]);
	while (m--){
		int op,x;
		scanf("%d%d",&op,&x);
		if (op==1){
			int y=find(x);
			printf("%u\n",qw(y)+s.query(in[x],in[x]));
		}
		else if (op==3){
			int y=find(x);
			ui ans=sum(cw,in[x],out[x])+(siz[x]-sum(c,in[x],out[x]))*qw(y);
			ans+=s.query(in[x],out[x]);
			printf("%u\n",ans);
		}
		else if (op==5){
			int y=find(x);
			ui w=qw(y),v=siz[x]-sum(c,in[x],out[x]);
			addc(in[y],-v);
			addc(in[x],v),addw(in[x],in[x],w);
			b[top[x]].insert(x);
			updmn(top[x]);
		}
		else if (op==6){
			int y=find(fa[x]);
			ui w=qw(x),v=qc(x);
			addc(in[y],+v);
			addc(in[x],-v);
//			addw(in[x],in[x],-w);	
			w=w-qw(y);
			s.add(in[x],out[x],w);
			addw(in[x],out[x],-w);
			b[top[x]].erase(x);
			updmn(top[x]);
		}
		else{
			ui v;
			scanf("%u",&v);
			if (op==2)
				addw(in[x],in[x],v);
			else
				addw(in[x],out[x],v);
		}
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM