作者:洋紫儿__来丶了 | 来源:互联网 | 2023-10-12 10:30
题目链接给定一棵\(n\)个点的树,每条边有一个边权,初始所有点都是黑点。\(q\)次操作,分为三种:将编号在\([l,r]\)范围内的点改成白点;将编号在\([l,r]\)范围内
题目链接
- 给定一棵 \(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
#define Tp template
#define Ts template
#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 vnamespace 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]>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;
}
败得义无反顾,弱得一无是处