WC2006水管局长 洛谷4172 bzoj2594 LCT

44 篇文章 0 订阅
12 篇文章 0 订阅

 

题目描述

SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了。嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项。在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗、消毒等等。嘟嘟在控制中心一声令下,这些水管的准备操作同时开始,但由于各条管道的长度、内径不同,进行准备操作需要的时间可能不同。供水公司总是希望嘟嘟能找到这样一条送水路径,路径上的所有管道全都准备就绪所需要的时间尽量短。嘟嘟希望你能帮助他完成这样的一个选择路径的系统,以满足供水公司的要求。另外,由于MY市的水管年代久远,一些水管会不时出现故障导致不能使用,你的程序必须考虑到这一点。不妨将MY市的水管网络看作一幅简单无向图(即没有自环或重边):水管是图中的边,水管的连接处为图中的结点。

题解:

题意相当于给你一个图,有一些询问,每次要删掉一条边或者询问x到y的所有路径中最长边最小是多少。

最大值最小的问题不是二分就考虑最小生成树。这题要最小生成树,但是是动态的,所以想到要用LCT维护。

但是做过魔法森林这道题之后我们知道,用LCT维护最小生成树时删边是不太容易的,而由由星球大战那道题我们可以想到可以离线加逆向查询,将删边改为连边,这样就能用LCT维护了。一开始做最小生成树的时候是用的并查集维护连通性。之后就变成了LCT维护动态最小生成树了。

写法上在数据加强版中我从某位神犇的博客中学到了一个离线时记录哪些边被删掉了方法,就是先保证每一条双向边的起点编号比终点小,之后对所有边进行排序,第一关键字是起点编号,第二关键字是终点编号,这样排序之后可以记录以每一个编号为起点的边在数组中的下标的左右区间,这样在离线时可以通过二分查找并打上标记的办法来记录哪些边是离线时被暂时删掉的。之后在预处理做最小生成树的排序时把第一关键字设为是否在离线时被标记过,第二关键字才是边长。这个做法比较神奇,我觉得值得掌握。

最后是代码,由于我自带大常数,在bzoj上25秒的时限跑了21秒多,所以如果参考我的代码的话一定注意常数优化。还有就是顺便吐槽一下,这题因为数组大小的问题我挂了好几次,最后看了一个题解的数组要开多大才调过的。。。

#include <bits/stdc++.h>
using namespace std;
//注意这种离线删边的写法 
int n,m,q,f[1100010],c[1100010][2],mx[1100010],rev[1100010],st[1100010];
int l[100010],r[100010],ans[200010],sz,val[1100010];
//li、ri表示以i节点为起点的边在a中排好序后的左右端点在哪,有了这个之后可以二分查找 
struct node
{
    int from,to,dis,opt,next;
}a[1000010],tt[100100],e[2200010];
int fa[100010],hed[1100010],cnt;//fa表示并查集中的父亲,f表示Splay中的父亲 
int ggg[1100010][2];//表示每个表示边的节点连的是哪两个点 
int read()
{
    int x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    ch=getchar();
    while(ch>='0'&&ch<='9')
    x=x*10+ch-'0',ch=getchar();
    return x;
}
int cmp1(node x,node y)
{
    if(x.from==y.from)
    return x.to<y.to;
    else
    return x.from<y.from;
}
//注意整数二分的写法 
void div(int le,int ri,int x,int i)
{
    int mid;
    while(le<=ri)
    {
        mid=(le+ri)>>1;
        if(a[mid].to==x)
        {
            a[mid].opt=1;
            tt[i].dis=a[mid].dis;
            return;
        }
        else if(a[mid].to<x)
        le=mid+1;
        else
        ri=mid-1;
    }
}
int cmp2(node x,node y)
{
    if(x.opt==y.opt)
    return x.dis<y.dis;
    else
    return x.opt<y.opt;
}
int getr(int x)
{
    if(x==fa[x])
    return x;
    else
    {
        fa[x]=getr(fa[x]);
        return fa[x];
    }
}
void add(int from,int to)
{
    e[++cnt].to=to;
    e[cnt].next=hed[from];
    hed[from]=cnt;
    e[++cnt].to=from;
    e[cnt].next=hed[to];
    hed[to]=cnt;
}
void dfs(int x,int ft)
{
    if(ft)
    f[x]=ft;
    for(int i=hed[x];i;i=e[i].next)
    {
        if(e[i].to!=ft)
        dfs(e[i].to,x);
    }
}
void pushup(int x)
{
    mx[x]=x;
    if(c[x][0])
    {
    	if(val[mx[x]]<val[mx[c[x][0]]])
    	mx[x]=mx[c[x][0]];
	}  
    if(c[x][1])
    {
    	if(val[mx[x]]<val[mx[c[x][1]]])
    	mx[x]=mx[c[x][1]];	
	}  
}
int nroot(int x)
{
    return c[f[x]][0]==x||c[f[x]][1]==x;
}
void pushdown(int x)
{
    if(rev[x])
    {
        swap(c[x][0],c[x][1]);
        rev[c[x][0]]^=1;
        rev[c[x][1]]^=1;
        rev[x]=0;
    }
}
void rotate(int x)
{
    int y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
    if(nroot(y))
    c[z][c[z][1]==y]=x;
    c[x][!k]=y;
    c[y][k]=w;
    if(w)
    f[w]=y;
    f[y]=x;
    f[x]=z;
    pushup(y);
    pushup(x);
}
void splay(int x)
{
    int y=x,z=0;
    st[++z]=y;
    while(nroot(y))
    {
        y=f[y];
        st[++z]=y;
    }
    while(z)
    pushdown(st[z--]);
    while(nroot(x))
    {
        int y=f[x],z=f[y];
        if(nroot(y))
        {
            if(c[z][0]==y ^ c[y][0]==x)
            rotate(y);
            else
            rotate(x);
        }
        rotate(x);
    }
    pushup(x);
}
void access(int x)
{
    int y=0;
    while(x!=0)
    {
        splay(x);
        c[x][1]=y;
        pushup(x);
        if(y!=0)
        f[y]=x;
        y=x;
        x=f[x]; 
    }
}
void makeroot(int x)
{
    access(x);
    splay(x);
    rev[x]^=1;
}
void link(int x,int y)
{
    makeroot(x);
    f[x]=y;
    splay(x);
}
void cut(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    c[y][0]=f[x]=0;
//  pushup(x);
}
int query(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    return mx[y];
}
int findroot(int x)
{
    access(x);
    splay(x);
    while(c[x][0])
    x=c[x][0];
    return x;
}
void ins(int x,int y,int z)
{
    if(findroot(x)==findroot(y)) 
    {
        int gg=query(x,y);
        if(val[gg]<=z)
        return;
        cut(gg,ggg[gg][0]);
        cut(gg,ggg[gg][1]);
    }
    ++sz;
    val[sz]=z;
    ggg[sz][0]=x;
    ggg[sz][1]=y;
    link(x,sz);
    link(y,sz);
}
int main()
{
    n=read();m=read();q=read();
    for(int i=1;i<=m;++i)
    {
        int x,y,z;
        x=read();
        y=read();
        z=read();
        if(x>y)
        swap(x,y);
        a[i].from=x;
        a[i].to=y;
        a[i].dis=z;
    }
    sort(a+1,a+m+1,cmp1);
    l[a[1].from]=1;
    r[a[m].from]=m;
    for(int i=2;i<=m;++i)
    {
        if(a[i-1].from!=a[i].from)
        {
            r[a[i-1].from]=i-1;
            l[a[i].from]=i;
        }
    }
    sz=n;
    for(int i=1;i<=q;++i)
    {
        int x,y;
        tt[i].opt=read();x=read();y=read();
        if(x>y)
        swap(x,y);
        tt[i].from=x;
        tt[i].to=y;
        if(tt[i].opt==2)
        {
            div(l[tt[i].from],r[tt[i].from],tt[i].to,i);
        }
    }  
    sort(a+1,a+m+1,cmp2);
    for(int i=1;i<=n;++i)
    fa[i]=i;
    for(int i=1;i<=m;++i)
    {
        if(a[i].opt)
        break;
        int fx=getr(a[i].from),fy=getr(a[i].to);
        if(fx!=fy)
        {
            ++sz;
            val[sz]=a[i].dis;
            ggg[sz][0]=a[i].from;
            ggg[sz][1]=a[i].to;
            add(a[i].from,sz);
            add(a[i].to,sz);
            fa[fx]=fa[fy];
        }
    }
    dfs(1,0);
    for(int i=q;i>=1;--i)
    {
        if(tt[i].opt==1)
        {
            int gg=query(tt[i].from,tt[i].to);
            ans[i]=val[gg]; 
        }
        else
        ins(tt[i].from,tt[i].to,tt[i].dis);
    }
    for(int i=1;i<=q;++i)
    {
        if(tt[i].opt==1)
        printf("%d\n",ans[i]);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值