P10776 Jabby's shadows

Luogu 链接

题意

有一棵 \(n\) 个点的无根树,边有正的边权,每个点只能是黑色或白色。
最初所有点均为黑色,黑色为 \(1\),白色为 \(2\)

现在有 \(q\) 个操作,操作有两种类型:

  • 询问点 \(u\) 所在树上同色连通块的直径,若为 \(0\),则输出 QwQ
  • 给定 \(u,v,c\),把 \(u\)\(v\) 这条链上的点都覆盖为颜色 \(c\)

数据范围:\(1\le n,q\le 10^5\)

思路

这个题一看就很 LCT,所以我们来考虑下都要维护些什么东西。


先考虑实链都要维护什么。

当询问时我们会将 \(u\) 换到根,所以要维护链上端点所在连通块的直径。
由于需要换根,所以我们需要同时维护上端点和下端点的信息。

类似最大子段和的思路,当两条实链合并时,直径需要取两条实链直径的最大值;
当下实链的上端点颜色与上实链的上端点的颜色相同时,需要再计算下实链上端点到连通块内最远点的距离,加上两点之间的边权,再加上上实链下端点到连通块最远点的距离,得到的答案与原来的答案取最大值。

这就要求我们再维护实链的上下端点、上下端点的颜色、上下端点到连通块的最远点的距离。

再仿照最大子段和的思路,当两条实链合并时,端点到连通块内最远点的距离先取对应实链的答案;
若有条实链上下是连通的,则需要计算该链上下端点的距离,加上两点之间边的距离,再加上另外一条实链的答案,所得到的答案与原来的答案取最大值。

这就要求我们再维护实链上下端点是否连通、上下端点之间的距离。

考虑到还有覆盖颜色的操作,所以还要维护当实链全为某种颜色时,上述的答案。

整理下,我们需要维护如下信息:

same:上下端点是否连通。
upV,downV:上下端点。
upC,downC:上下端点的颜色。
dis:上下端点之间的距离。
upFar,downFar:上下端点到所在连通块最远点的距离。
upD,downD:上下端点所在连通块的直径。
upFar0,upFar1,downFar0,downFar1:当实链颜色全为白或黑时,上下端点到所在连通块最远点的距离。
D0,D1:当实链颜色全为白或黑时,该实链所在连通块的直径。

当需要翻转实链时,把上下端点的信息交换;
当需要将实链染色,把信息赋值成对应颜色的信息,并将 same 赋值为 true


再考虑需要维护虚子树的什么信息。

若一个实链只有一个点,他的信息就需要由虚子树的信息计算出来。

此时直径有两种方式得到:

  • 虚子树中和点颜色相同的该点所在连通块的直径的最大值。
  • 一条或两条虚子树中和该点颜色相同的点到所在连通块的最远点的路径加上虚子树的端点到该点的边,所组成的路径的长度,即维护所在连通块到该点距离的最大值、次大值。(我是用 multiset 维护的)

然后我们发现其他信息也可以通过上述信息得出。

因此我们需要维护下述信息:

vD[2]:虚子树上端点颜色为白色或黑色所在连通块的直径。
vFar[2]:虚子树上端点颜色为白色或黑色所在连通块最远点到该点的距离。


我们接下来考虑 LCT 的操作。

pushup 时,先根据虚子树的信息得到单点的信息,再把信息和左儿子、右儿子合并;
accesslink 时,维护虚子树的信息;
当要翻转链、做链覆盖时,打上标记,并维护实链的信息。

别的就没有什么要注意了。

程序

AC 记录

#include<bits/stdc++.h>
#define forUp(i,a,b) for(int i=(a);i<=(b);++i)
#define forUP(i,a,b) for(int i=(a);i<(b);++i)
#define forDown(i,a,b) for(int i=(a);i>=(b);--i)
#define forG(u,v) for(int __i=head[u],v=to[__i];__i;__i=nxt[__i],v=to[__i])
#define pb emplace_back
using ll=long long;using ull=unsigned long long;using uint=unsigned int;using db=double;using ld=long db;using pii=std::pair<int,int>;using pdi=std::pair<db,int>;using vl=__int128;using uvl=unsigned __int128;
constexpr int INF=0x3f3f3f3f,MINF=0xcfcfcfcf;constexpr long long INFLL=0x3f3f3f3f3f3f3f3f,MINFLL=0xcfcfcfcfcfcfcfcf;constexpr double INFDB=1e50,eps=1e-9;
template<class _Tp>void chkMax(_Tp &x,const _Tp &y){x<y?x=y:0;}template<class _Tp>void chkMin(_Tp &x,const _Tp &y){x>y?x=y:0;}
constexpr int N=1e5+10;int __test_num=1,__test_id;using namespace std;void __init();

int n,parent[N],weight[N],q,op,u,v,c;

namespace LCT{
	struct Heap{
		multiset<int> S;
		void push(int x){S.insert(x);}
		void pop(int x){auto it=S.find(x);if(it!=S.end())S.erase(it);}
		int top(){if(S.empty())return 0;return *--S.end();}
		int top2(){if(S.empty())return 0;if(S.size()==1)return *--S.end();return *--S.end()+*----S.end();}
	};
	int getWeight(int downV,int upV){
		if(parent[upV]==downV)swap(upV,downV);
		return weight[downV];
	}
	struct realChain{
		bool same;int upV,downV,upC,downC,dis,upFar,downFar,upD,downD,upFar0,upFar1,downFar0,downFar1,D0,D1;
		friend realChain operator+(const realChain &downChain,const realChain &upChain){
			int dis=getWeight(downChain.upV,upChain.downV);
			realChain mergeChain;
			mergeChain.same=downChain.same&&upChain.same&&downChain.upC==upChain.downC;
			mergeChain.downV=downChain.downV;
			mergeChain.upV=upChain.upV;
			mergeChain.downC=downChain.downC;
			mergeChain.upC=upChain.upC;
			mergeChain.dis=downChain.dis+dis+upChain.dis;
			mergeChain.downFar=downChain.downFar;
			mergeChain.upFar=upChain.upFar;
			mergeChain.downD=downChain.downD;
			mergeChain.upD=upChain.upD;
			if(downChain.upC==upChain.downC){
				if(downChain.same){
					chkMax(mergeChain.downD,max(downChain.upFar+dis+upChain.downFar,upChain.downD));
					chkMax(mergeChain.downFar,downChain.dis+dis+upChain.downFar);
				}
				if(upChain.same){
					chkMax(mergeChain.upD,max(downChain.upFar+dis+upChain.downFar,downChain.upD));
					chkMax(mergeChain.upFar,downChain.upFar+dis+upChain.dis);
				}
			}
			mergeChain.upFar0=max(upChain.upFar0,downChain.upFar0+dis+upChain.dis);
			mergeChain.upFar1=max(upChain.upFar1,downChain.upFar1+dis+upChain.dis);
			mergeChain.downFar0=max(downChain.downFar0,downChain.dis+dis+upChain.downFar0);
			mergeChain.downFar1=max(downChain.downFar1,downChain.dis+dis+upChain.downFar1);
			mergeChain.D0=max(max(downChain.D0,upChain.D0),downChain.upFar0+dis+upChain.downFar0);
			mergeChain.D1=max(max(downChain.D1,upChain.D1),downChain.upFar1+dis+upChain.downFar1);
			return mergeChain;
		}
		void modifyRev(){
			swap(upV,downV);
			swap(upC,downC);
			swap(upFar,downFar);
			swap(upD,downD);
			swap(upFar0,downFar0);
			swap(upFar1,downFar1);
		}
		void modifyCov(int col){
			same=true;
			upC=downC=col;
			if(col==0)upFar=upFar0,downFar=downFar0,upD=downD=D0;
			if(col==1)upFar=upFar1,downFar=downFar1,upD=downD=D1;
		}
		void init(int node){
			same=true;
			upV=downV=node;
			upC=downC=1;
			dis=upFar=downFar=upD=downD=upFar0=upFar1=downFar0=downFar1=D0=D1=0;
		}
	};
	int fa[N],son[N][2],col[N];realChain data[N];int cov[N];bool rev[N];Heap vFar[N][2],vD[N][2];
	void init(int node){
		col[node]=1;
		data[node].init(node);
		cov[node]=-1;
	}
	void init(){forUp(node,1,n)init(node);}
	bool nroot(int node){return son[fa[node]][0]==node||son[fa[node]][1]==node;}
	void pushup(int node){
		data[node].same=true;
		data[node].upV=data[node].downV=node;
		data[node].upC=data[node].downC=col[node];
		data[node].dis=0;
		data[node].upFar=data[node].downFar=vFar[node][col[node]].top();
		data[node].upD=data[node].downD=max(vD[node][col[node]].top(),vFar[node][col[node]].top2());
		data[node].upFar0=data[node].downFar0=vFar[node][0].top();
		data[node].upFar1=data[node].downFar1=vFar[node][1].top();
		data[node].D0=max(vD[node][0].top(),vFar[node][0].top2());
		data[node].D1=max(vD[node][1].top(),vFar[node][1].top2());
		if(son[node][0])data[node]=data[node]+data[son[node][0]];
		if(son[node][1])data[node]=data[son[node][1]]+data[node];
	}
	void modifyRev(int node){
		rev[node]^=1;
	    swap(son[node][0],son[node][1]);
	    data[node].modifyRev();
	}
	void modifyCov(int node,int color){
		col[node]=cov[node]=color;
		data[node].modifyCov(color);
	}
	void pushdown(int node){
		if(rev[node]){
			if(son[node][0])modifyRev(son[node][0]);
			if(son[node][1])modifyRev(son[node][1]);
			rev[node]=0;
		}
		if(cov[node]!=-1){
			if(son[node][0])modifyCov(son[node][0],cov[node]);
			if(son[node][1])modifyCov(son[node][1],cov[node]);
			cov[node]=-1;
		}
	}
	void rotate(int node){
		int father=fa[node],grand=fa[father],dir=son[father][1]==node,tmp=son[node][!dir];
		if(nroot(father))son[grand][son[grand][1]==father]=node;
		son[node][!dir]=father,son[father][dir]=tmp;
		if(tmp)fa[tmp]=father;
		fa[father]=node,fa[node]=grand;
		pushup(father);pushup(node);
	}
	void clearTag(int node){
		if(nroot(node))clearTag(fa[node]);
		pushdown(node);
	}
	void splay(int node){
		clearTag(node);
		while(nroot(node)){
			int father=fa[node],grand=fa[father];
			if(nroot(father))rotate((son[father][0]==node)^(son[grand][0]==father)?node:father);
			rotate(node);
		}
		pushup(node);
	}
	int access(int node){
		int tmp=0;
		for(;node;node=fa[tmp=node]){
			splay(node);
			if(son[node][1]){
				vFar[node][data[son[node][1]].upC].push(data[son[node][1]].upFar+getWeight(data[son[node][1]].upV,node));
				vD[node][data[son[node][1]].upC].push(data[son[node][1]].upD);
			}
			son[node][1]=tmp;
			if(son[node][1]){
				vFar[node][data[son[node][1]].upC].pop(data[son[node][1]].upFar+getWeight(data[son[node][1]].upV,node));
				vD[node][data[son[node][1]].upC].pop(data[son[node][1]].upD);
			}
			pushup(node);
		}
		return tmp;
	}
	void makeRoot(int node){
		access(node);splay(node);
		modifyRev(node);
	}
	void split(int node1,int node2){
		makeRoot(node1);
		access(node2);splay(node2);
	}
	void link(int node1,int node2){
		makeRoot(node1),makeRoot(node2);
		fa[node2]=node1;
		vFar[node1][col[node2]].push(data[node2].upFar+getWeight(node2,node1));
		vD[node1][col[node2]].push(data[node2].upD);
	}
	void build(){
		init();
		forUp(node,2,n)link(parent[node],node);
	}
	void update(int node1,int node2,int color){
		split(node1,node2);
		modifyCov(node2,color);
	}
	int query(int node){
		makeRoot(node);
		return data[node].upD;
	}
}using LCT::build,LCT::update,LCT::query;

void __solve(int __test_id){
	scanf("%d",&n);
	forUp(node,2,n)scanf("%d",&parent[node]);
	forUp(node,2,n)scanf("%d",&weight[node]);
	build();
	scanf("%d",&q);
	while(q--){
		scanf("%d%d",&op,&u);
		if(op==1){
			int ans=query(u);
			if(ans==0)printf("QwQ\n");
			else printf("%d\n",ans);
		}
		else{
			scanf("%d%d",&v,&c);
			update(u,v,c&1);
		}
	}
}
signed main(){
	__init();
	forUp(i,1,__test_num)__solve(i);
	return 0;
}
void __init(){
	//const string __file_name="test";freopen((__file_name+".in").c_str(),"r",stdin);freopen((__file_name+".out").c_str(),"w",stdout);
	//scanf("%d",&__test_num);
}
posted @ 2025-08-27 18:23  LXcjh4998  阅读(6)  评论(0)    收藏  举报