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;
}
posted @ 2020-11-17 19:03  jz_597  阅读(288)  评论(4编辑  收藏  举报