把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF1628E】Groceries in Meteor Town(Kruskal重构树+线段树)

题目链接

  • 给定一棵 \(n\) 个点的树,每条边有一个边权,初始所有点都是黑点。
  • \(q\) 次操作,分为三种:将编号在 \([l,r]\) 范围内的点改成白点;将编号在 \([l,r]\) 范围内的点改成黑点;询问从一个点出发到所有白点的简单路径上的最大边权。
  • \(2\le n,q\le3\times10^5\)

Kruskal 重构树

询问从一个点到所有白点的简单路径上的最大边权,其实就是问由这个点和所有白点构成的虚树上的最大边权。

也就是询问一个最小的权值,满足仅保留小于等于这个值的边时这个点能与所有白点连通。

这其实就是求这些点在 Kruskal 重构树中的 \(\operatorname{LCA}\) 的权值。

要求若干点 \(\operatorname{LCA}\),实际上只要知道其中 dfs 序最小的点和 dfs 序最大的点,求出它们的 \(\operatorname{LCA}\) 即可。

所以用一棵线段树维护最小 dfs 序和最大 dfs 序。

事先预处理出每个节点表示的区间内所有 dfs 序的最小值和最大值,这样区间染白的时候就可以直接调用信息。

代码:\(O(n\log n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 300000
#define LN 19
using namespace std;
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
	I void NA() {pc('-'),pc('1'),pc('\n');} 
}using namespace FastIO;
int n;struct line {int x,y,v;bool operator < (Cn line& o) Cn {return v<o.v;}}s[N+5];
namespace K//Kruskal重构树
{
	int ct,V[N<<1],S[N<<1][2],d,dfn[N<<1],fac[N<<1],D[N<<1],f[N<<1][LN+1];
	int fa[N+5];int Fa(CI x) {return fa[x]?fa[x]=Fa(fa[x]):x;}//并查集
	void dfs(CI x,CI p=0) {D[x]=D[f[x][0]=p]+1,fac[dfn[x]=++d]=x,x>n&&(dfs(S[x][0],x),dfs(S[x][1],x),0);}//dfs
	I void Bd()//建树+预处理
	{
		RI i,j;for(sort(s+1,s+n),ct=n,i=1;i^n;++i) V[++ct]=s[i].v,fa[S[ct][0]=Fa(s[i].x)]=fa[S[ct][1]=Fa(s[i].y)]=ct;//Kruskal建树
		for(dfs(ct),j=1;j<=LN;++j) for(i=1;i<=ct;++i) f[i][j]=f[f[i][j-1]][j-1];//倍增预处理
	}
	I int LCA(RI x,RI y)//倍增LCA
	{
		RI i;for(D[x]<D[y]&&(swap(x,y),0),i=0;D[x]^D[y];++i) (D[x]^D[y])>>i&1&&(x=f[x][i]);if(x==y) return x;
		for(i=0;f[x][i]^f[y][i];++i);for(--i;~i;--i) f[x][i]^f[y][i]&&(x=f[x][i],y=f[y][i]);return f[x][0];
	}
}
class SegmentTree//线段树
{
	private:
		#define PT RI l=1,RI r=n,RI o=1
		#define LT l,u,o<<1
		#define RT u+1,r,o<<1|1
		#define PU(o) (Mn[o]=min(Mn[o<<1],Mn[o<<1|1]),Mx[o]=max(Mx[o<<1],Mx[o<<1|1]))
		#define PD(o) (~P[o]&&(T(o<<1,P[o]),T(o<<1|1,P[o]),P[o]=-1))
		#define T(o,v) ((P[o]=v)?(Mn[o]=Mn0[o],Mx[o]=Mx0[o]):(Mn[o]=1e9,Mx[o]=0))//染色修改
		int P[N<<2],Mn[N<<2],Mx[N<<2],Mn0[N<<2],Mx0[N<<2];
	public:
		void Bd(PT)
		{
			if(Mn[o]=1e9,Mx[o]=0,l==r) return (void)(Mn0[o]=Mx0[o]=K::dfn[l]);RI u=l+r>>1;Bd(LT),Bd(RT);
			Mn0[o]=min(Mn0[o<<1],Mn0[o<<1|1]),Mx0[o]=max(Mx0[o<<1],Mx0[o<<1|1]);//预处理区间内最小值和最大值
		}
		void U(CI L,CI R,CI v,PT)//区间染色
		{
			if(L<=l&&r<=R) return (void)T(o,v);RI u=l+r>>1;PD(o);L<=u&&(U(L,R,v,LT),0),R>u&&(U(L,R,v,RT),0),PU(o);
		}
		I int G(CI x) Cn//加入x询问
		{
			RI mn=min(Mn[1],K::dfn[x]),mx=max(Mx[1],K::dfn[x]);return K::V[K::LCA(K::fac[mn],K::fac[mx])];//询问dfs序最小和最大的点的LCA
		}
}S;
int main()
{
	RI Qt,i;for(read(n,Qt),i=1;i^n;++i) read(s[i].x,s[i].y,s[i].v);K::Bd();
	RI op,x,y,t;S.Bd();W(Qt--) switch(read(op),op)
	{
		case 1:read(x,y),S.U(x,y,1);break;case 2:read(x,y),S.U(x,y,0);break;
		case 3:read(x),(t=S.G(x))?writeln(t):NA();break;
	}return clear(),0;
}
posted @ 2022-03-06 15:15  TheLostWeak  阅读(66)  评论(0编辑  收藏  举报