[省选前题目整理][BZOJ 2594]管道局长数据加强版(LCT)

142 篇文章 0 订阅
7 篇文章 0 订阅

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=2594

思路

很显然可以用LCT搞搞。。。正着做是删边,倒着做就是加边了。。。
而且可以发现,两个点之间最大边权最小的路径一定是在当前的MST上,因此我们离线把询问中删掉的边都无视掉后,在最后剩下的边里生成MST,并用一个LCT去维护这个MST,就是一条边看成LCT里的一个结点,MST上的一个点也看成LCT上的一个结点,一条边的两个端点对应的结点向这条边对应的结点连边,并且给每个边对应的结点打上路径最大值的tag,倒着从最后一个询问到第一个询问做,每次删边就变成了加边,如果当前要加入的边比这个边两端点路径上的最大边权要小,那么把那个最大边权的边在LCT里cut掉,然后link上当前要加入的边。

反正和NOI 2014魔法森林的LCT做法很像。。。个人认为LCT维护动态MST的做法非常经典。。。

另外此题还需要对边排序,并二分,得到每次删除询问要删的边的编号,我偷懒直接用的map,第一次交就TLE了,后来玩卡空间+卡时间过掉了,差点TLE+MLE,比标准的二分做法慢了4s,因此第一次做的同学们就别学我这做法了,2333

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <map>

#define MAXN 1150000
#define lson ch[o][0]
#define rson ch[o][1]

using namespace std;

map<int,int>mp[100100];
bool mark[MAXN];

inline int getint()
{
    char ch = getchar();
    for ( ; ch > '9' || ch < '0'; ch = getchar());
    int tmp = 0;
    for ( ; '0' <= ch && ch <= '9'; ch = getchar())
      tmp = tmp * 10 + int(ch) - 48;
    return tmp;
}

int ch[MAXN][2],fa[MAXN],val[MAXN],maxtag[MAXN],stack[MAXN],top=0;
bool rev[MAXN];

inline void pushup(int o)
{
    maxtag[o]=o;
    if(val[maxtag[lson]]>val[maxtag[o]]) maxtag[o]=maxtag[lson];
    if(val[maxtag[rson]]>val[maxtag[o]]) maxtag[o]=maxtag[rson];
}

inline void pushdown(int o)
{
    if(!o) return;
    if(rev[o])
    {
        rev[lson]^=1;
        rev[rson]^=1;
        swap(lson,rson);
        rev[o]=0;
    }
}

inline bool isRoot(int o)
{
    return ch[fa[o]][0]!=o&&ch[fa[o]][1]!=o;
}

inline void rot(int x)
{
    int y=fa[x],z=fa[y];
    int p,q;
    if(ch[y][0]==x) p=0;
    else p=1;
    q=p^1;
    if(!isRoot(y))
    {
        if(ch[z][0]==y) ch[z][0]=x;
        else ch[z][1]=x;
    }
    fa[x]=z,fa[y]=x;
    ch[y][p]=ch[x][q];
    fa[ch[x][q]]=y;
    ch[x][q]=y;
    pushup(y);
    pushup(x);
}

inline void splay(int x)
{
    top=0;
    stack[++top]=x;
    for(int i=x;!isRoot(i);i=fa[i]) stack[++top]=fa[i];
    for(int i=top;i>=1;i--) pushdown(stack[i]); //!!!!!!
    while(!isRoot(x))
    {
        int y=fa[x],z=fa[y];
        if(!isRoot(y))
        {
            if((ch[y][0]==x)==(ch[z][0]==y)) rot(y);
            else rot(x);
        }
        rot(x);
    }
}

inline void access(int x)
{
    int tmp=0;
    while(x)
    {
        splay(x);
        ch[x][1]=tmp;
        pushup(x);
        tmp=x;
        x=fa[x];
    }
}

inline void makeroot(int x)
{
    access(x);
    splay(x);
    rev[x]^=1;
}

inline void link(int x,int y) //y是x父亲
{
    makeroot(x);
    fa[x]=y;
}

inline void cut(int x,int y) //y是x父亲
{
    makeroot(x);
    access(y);
    splay(y);
    ch[y][0]=fa[x]=0;
}

inline int query(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    return maxtag[y];
}

int f[100100];

inline int findSet(int x)
{
    if(f[x]==x) return f[x];
    return f[x]=findSet(f[x]);
}

struct edge
{
    int u,v,w;
}edges[1000100];

inline bool cmp(edge a,edge b)
{
    return a.w<b.w;
}

int n,m,q;

struct Query
{
    int x,y,cmd,ans;
}querys[100100];

int main()
{
    for(int i=0;i<MAXN;i++) f[i]=i;
    n=getint(),m=getint(),q=getint();
    for(int i=1;i<=m;i++)
        edges[i].u=getint(),edges[i].v=getint(),edges[i].w=getint();
    for(int i=1;i<=q;i++)
        querys[i].cmd=getint(),querys[i].x=getint(),querys[i].y=getint();
    sort(edges+1,edges+m+1,cmp);
    for(int i=1;i<=m;i++)
        mp[edges[i].u][edges[i].v]=mp[edges[i].v][edges[i].u]=i;
    for(int i=1;i<=q;i++)
        if(querys[i].cmd==2)
            mark[mp[querys[i].x][querys[i].y]]=1;
    for(int i=1;i<=m;i++) //初始化LCT结点
    {
        val[n+i]=edges[i].w;
        maxtag[n+i]=n+i;
    }
    int tot=0; //加的边的个数,为n-1时表明整个MST生成出来了
    for(int i=1;i<=m;i++)
    {
        if(mark[i]) continue;
        int rootu=findSet(edges[i].u),rootv=findSet(edges[i].v);
        if(rootu!=rootv)
        {
            f[rootu]=rootv;
            link(edges[i].u,i+n);
            link(edges[i].v,i+n);
            tot++;
            if(tot==n-1) break;
        }
    }
    for(int i=q;i>=1;i--)
    {
        if(querys[i].cmd==1)
            querys[i].ans=val[query(querys[i].x,querys[i].y)];
        else //加边操作
        {
            int p=mp[querys[i].x][querys[i].y];
            int tmp=query(querys[i].x,querys[i].y);
            if(val[tmp]>edges[p].w)
            {
                cut(edges[tmp-n].u,tmp);
                cut(edges[tmp-n].v,tmp);
                link(edges[p].u,p+n);
                link(edges[p].v,p+n);
            }
        }
    }
    for(int i=1;i<=q;i++)
        if(querys[i].cmd==1)
            printf("%d\n",querys[i].ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值