P4172 [WC2006]水管局长

传送门

显然 $LCT$ 动态维护最小生成树

询问就是问树上两点的路径中权值最大的边

为了维护边权,我们要把边也看成点,为了方便,边在 $LCT$ 中的编号为 $n+1$ 到 $n+m$

因为正做不好维护删边,所以离线倒过来,变成加边

在反过来做的时候,为了维护最小生成树要知道哪些边被删除了

用 $set$ 或者 $map$ 并不好维护,但是因为点数不大,所以直接暴力开二维数组就好

注意洛谷数据中可能给的边是 $(u,v)$ ,但是删除的边是 $(v,u)$

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2e5+7;
int c[N][2],fa[N],t[N],val[N];//t维护的是最大值的编号,val是本身的值
bool rev[N];
inline void pushup(int x)
{
    t[x]=x;
    if(val[ t[c[x][0]] ] > val[t[x]]) t[x]=t[c[x][0]];
    if(val[ t[c[x][1]] ] > val[t[x]]) t[x]=t[c[x][1]];
}
inline void pushdown(int x)
{
    if(!x||!rev[x]) return;
    int &lc=c[x][0],&rc=c[x][1];
    swap(lc,rc); rev[x]=0;
    if(lc) rev[lc]^=1;
    if(rc) rev[rc]^=1;
}
inline void rever(int x) { rev[x]^=1; pushdown(x); }
inline bool noroot(int x) { return (c[fa[x]][0]==x)|(c[fa[x]][1]==x); }
inline void rotate(int x)
{
    int y=fa[x],z=fa[y],d=(c[y][1]==x);
    if(noroot(y)) c[z][c[z][1]==y]=x;
    fa[x]=z; fa[y]=x; fa[c[x][d^1]]=y;
    c[y][d]=c[x][d^1]; c[x][d^1]=y;
    pushup(y); pushup(x);
}
inline void push_tag(int x)
{
    if(noroot(x)) push_tag(fa[x]);
    else pushdown(x);
    pushdown(c[x][0]); pushdown(c[x][1]);
}
inline void splay(int x)
{
    push_tag(x);
    while(noroot(x))
    {
        int y=fa[x],z=fa[y];
        if(noroot(y))
        {
            if(c[z][0]==y ^ c[y][0]==x) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}
inline void access(int x)
{
    for(int y=0;x;y=x,x=fa[x])
        splay(x),c[x][1]=y,pushup(x);
}
inline void makeroot(int x) { access(x); splay(x); rever(x); }
inline int findroot(int x)
{
    access(x); splay(x); pushdown(x);
    while(c[x][0]) x=c[x][0],pushdown(x);
    splay(x);
    return x;
}
inline int split(int x,int y) { makeroot(x); access(y); splay(y); return t[y]; }//返回路径上最大边的编号
inline void link(int x,int y) { makeroot(x); if(findroot(y)!=x) fa[x]=y; }
inline void cut(int x,int y) { split(x,y); c[y][0]=fa[x]=0; pushup(y); }
//以上为LCT模板

int n,m,Q,ans[N];
int mp[1007][1007],id[N],opt[N],dx[N],dy[N];//mp[u][v]维护边(u,v)的编号,id[i]维护第i次操作删的边
//opt,dx,dy存读入数据
bool p[N];//判断边是否被删除
struct edge{
    int x,y,v;
}e[N];//存边
inline void insert(int k)//加入编号为k的边
{
    int x=e[k].x,y=e[k].y; bool flag=1;
    if(findroot(x)==findroot(y))
    {
        int w=split(x,y);
        if(val[w]>e[k].v) cut(w,e[w-n].x),cut(w,e[w-n].y);//如果更大,删边
        else flag=0;
    }
    if(flag) link(x,n+k),link(y,n+k);//连边
}
int main()
{
    int a,b;
    n=read(),m=read(),Q=read();
    for(int i=1;i<=m;i++)
    {
        a=e[i].x=read(),b=e[i].y=read(),e[i].v=read();
        mp[a][b]=mp[b][a]=i; val[n+i]=e[i].v;
    }
    for(int i=1;i<=Q;i++)
    {
        opt[i]=read(),dx[i]=a=read(),dy[i]=b=read();
        if(opt[i]==2)
            p[mp[a][b]]=1,id[i]=mp[a][b];
    }
    for(int i=1;i<=m;i++) if(!p[i]) insert(i);
    for(int i=Q;i>=1;i--)
    {
        if(opt[i]==1) { ans[i]=val[ split(dx[i],dy[i]) ]; continue; }
        insert(id[i]);
    }
    for(int i=1;i<=Q;i++) if(opt[i]==1) printf("%d
",ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/LLTYYC/p/10660959.html