【BZOJ2594】水管局长加强版,LCT+并查集+二分查找位置

Time:2016.05.10

Author:xiaoyimi

转载注明出处谢谢



传送门

思路:

LCT维护路径最小值

倒叙处理询问,就相当于往图里面加边。

实时维护最小值,即最小生成树,可以参照魔法森林。

最初的最小生成树操作用kruskal

最蛋疼的是处理询问时你不知道要删除哪条边,这给kruskal带来很大麻烦,所以我们对原来的每一条边使其编号小的端点在前,大的在后,然后以左端点为第一关键字,右端点为第二关键字排序,记录下每个左端点的所在区间,然后就可以通过二分查找的方式来确定是哪条边了

LCT操作时把边当成带有权值的点,每次处理询问时看两点是否联通,不连通就连上,连通就寻找路径上的最大值,两者比较取较小即可,连通性用并查集维护(LCT其实也可以,不过复杂度略大)

注意:

说起来容易做起来难,这道题调了我好久!是一道可以练习码力的题目!

代码:

#include<bits/stdc++.h>
using namespace std;
#define N 100003
#define M 1000003
int in()
{
    int t=0;char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) t=(t<<3)+(t<<1)+ch-48,ch=getchar();
    return t;
}
int n=in(),m=in(),k=in();
int belong[N],L[N],R[N],ans[N],stacks[N+M],X[M],Y[M];
bool vis[M];
struct edge
{
    int id,u,v,w;
    void swaps(){if (u>v) swap(u,v);}
}e[M];
struct LCT
{
    int data,maxn,ch[2],fa;
    bool lazy;
}a[M+N];
struct question
{
    int opt,x,y,id,w;
    void swaps(){if (x>y) swap(x,y);}
}q[N];
bool cmp1(edge x,edge y)
{
    if (x.u==y.u) return x.v<y.v;
    return x.u<y.u;
}
bool cmp2(edge x,edge y){return x.w<y.w;}
int find(int x)
{
    if (belong[x]!=x) belong[x]=find(belong[x]);
    return belong[x];
}
edge search(int l,int r,int data)
{
    int mid;
    while (l<=r)
    {
        mid=l+r>>1;
        if (e[mid].v==data) return e[mid];
        if (e[mid].v>data) r=mid-1;
        else l=mid+1;
    }
}
void ct(int x)
{
    int l=a[x].ch[0],r=a[x].ch[1];
    a[x].maxn=(a[a[l].maxn].data>a[a[r].maxn].data?a[l].maxn:a[r].maxn);
    a[x].maxn=(a[a[x].maxn].data>a[x].data?a[x].maxn:x);
}
void pushdown(int x)
{
    if (!a[x].lazy) return;
    a[a[x].ch[0]].lazy^=1;
    a[a[x].ch[1]].lazy^=1;
    swap(a[x].ch[0],a[x].ch[1]);
    a[x].lazy=0;
}
bool isroot(int x)
{
    return a[a[x].fa].ch[0]!=x&&a[a[x].fa].ch[1]!=x;
}
void rorate(int x,bool mk)
{
    int y=a[x].fa;
    a[x].fa=a[y].fa;
    if (!isroot(y))
    {
        if (a[a[y].fa].ch[0]==y) a[a[y].fa].ch[0]=x;
        else a[a[y].fa].ch[1]=x;
    }
    a[y].ch[!mk]=a[x].ch[mk];
    a[a[x].ch[mk]].fa=y;
    a[y].fa=x;
    a[x].ch[mk]=y;
    ct(y);ct(x);
}
void splay(int x)
{
    int y,cnt=0,k=x;
    stacks[++cnt]=k;
    while (!isroot(k)) k=a[k].fa,stacks[++cnt]=k;
    for (int i=cnt;i;i--) pushdown(stacks[i]);
    while (!isroot(x))
    {
        y=a[x].fa;
        if (isroot(y))
        {
            if (a[y].ch[0]==x) rorate(x,1);
            else rorate(x,0);
        }
        else if (a[a[y].fa].ch[0]==y)
        {
            if (a[y].ch[0]==x) rorate(y,1);
            else rorate(x,0);
            rorate(x,1);
        }
        else
        {
            if (a[y].ch[1]==x) rorate(y,0);
            else rorate(x,1);
            rorate(x,0);
        }
    }
}
void access(int x)
{
    for (int y=0;x;y=x,x=a[x].fa)
        splay(x),
        a[x].ch[1]=y,
        ct(x);
}
void link(int x,int y)
{
    access(x);
    splay(x);
    a[x].lazy^=1;
    a[x].fa=y;
    splay(x);
}
void cut(int x,int y)
{
    access(x);
    splay(x);
    a[x].lazy^=1;
    access(y);
    splay(y);
    a[x].fa=a[y].ch[0]=0;
    ct(y);
}
int ask(int x,int y)
{
    access(x);
    splay(x);
    a[x].lazy^=1;
    access(y);
    splay(y);
    return a[y].maxn;
}
main()
{
    for (int i=0;i<=n;i++)
        belong[i]=i;
    for (int i=1;i<=m;i++)
        e[i]=(edge){i,in(),in(),in()},
        a[i+n].data=e[i].w,
        e[i].swaps(),X[i]=e[i].u,Y[i]=e[i].v;
    for (int i=1;i<=k;i++)
        q[i].opt=in(),q[i].x=in(),q[i].y=in(),
        q[i].swaps();
    sort(e+1,e+m+1,cmp1);
    L[e[1].u]=1;
    for (int i=1;i<m;i++)
        if (e[i].u!=e[i+1].u) R[e[i].u]=i,L[e[i+1].u]=i+1;
    R[e[m].u]=m;
    edge re;
    for (int i=1;i<=k;i++)
        if (q[i].opt==2)
            re=search(L[q[i].x],R[q[i].x],q[i].y),
            q[i].id=re.id,q[i].w=re.w,
            vis[q[i].id]=1;
    sort(e+1,e+m+1,cmp2);
    int t=0;
    for (int i=1;i<=m;i++)
    if (!vis[e[i].id])
    {
        int f1=find(e[i].u),f2=find(e[i].v);
        if (f1!=f2)
        {
            belong[f1]=f2;
            link(e[i].u,e[i].id+n);
            link(e[i].v,e[i].id+n);
            if (++t==n-1) break;
        }
    }
    for (int i=k;i;i--)
        if (q[i].opt==2)
        {
            int t=ask(q[i].x,q[i].y);
            if (a[t].data>q[i].w)
                cut(X[t-n],t),
                cut(Y[t-n],t),
                link(q[i].x,q[i].id+n),
                link(q[i].y,q[i].id+n);
        }
        else
            ans[++ans[0]]=a[ask(q[i].x,q[i].y)].data;
    for (int i=ans[0];i;i--) printf("%d\n",ans[i]);
}
时间: 2024-07-30 10:11:50

【BZOJ2594】水管局长加强版,LCT+并查集+二分查找位置的相关文章

普林斯顿公开课 算法1-8:并查集 快速查找

本节讲的是并查集的第一种实现方法,这种方法查找操作开销很小而合并操作开销比较大. 数据结构 假设有N个节点,那么该算法的数据结构就是一个包含N个整数的数组id[]. 判断操作 判断节点p和节点q是否相连就是判断id[p]和id[q]的值是否一致. 合并操作 合并节点p和节点q就是将id数组中所有的id[p]都修改为id[q]. 这样的话,每次合并都要遍历整个数组,修改多个值,因此开销比较大. 复杂度 合并一次的复杂度是N,如果需要合并N次,那么整个程序的复杂度就是N^2.这样的复杂度不适合应用于

BZOJ2594水管局长数据加强版

Description SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了.嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项. 在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗.消毒等等.嘟嘟在

【BZOJ4998】星球联盟 LCT+并查集

[BZOJ4998]星球联盟 Description 在遥远的S星系中一共有N个星球,编号为1…N.其中的一些星球决定组成联盟,以方便相互间的交流.但是,组成联盟的首要条件就是交通条件.初始时,在这N个星球间有M条太空隧道.每条太空隧道连接两个星球,使得它们能够相互到达.若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有公共隧道的路径.为了壮大联盟的队伍,这些星球将建设P条新的太空隧道.这P条新隧道将按顺序依次建成.一条新轨道建成后,可能会使一些星球属于同一

bzoj2959: 长跑(LCT+并查集)

题解 动态树Link-cut tree(LCT)总结 LCT常数大得真实 没有环,就是\(lct\)裸题吧 有环,我们就可以绕环转一圈,缩点 怎么搞? 当形成环时,把所有点的值全部加到一个点上,用并查集维护加到哪个点上 判断连通性再用一个并查集 Code #include<bits/stdc++.h> #define LL long long #define RG register using namespace std; template<class T> inline void

bzoj2959: 长跑 LCT+并查集+边双联通

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=2959 题解 调了半天,终于调完了. 显然题目要求是求出目前从 \(A\) 到 \(B\) 的可以经过重复的点(权值只算一次)的最长路. 考虑把无向图边双缩点以后,形成一棵树的关系.可以发现,边双内的点的点权是可以都取到的.所以边双缩点以后直接求出树上两个点之间的点权之和就可以了. 但是考虑怎么维护这个边双. 新链接一条边的时候,如果两个端点不连通,那么直接连上就可以了. 如果两个端点联通,那

bzoj4998 星球联盟 LCT + 并查集

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4998 题解 根据题意,就是要动态维护点双,求出一个点双的权值和. 所以这道题就是和 bzoj2959 长跑 一样了,每一次枚举一条路径上的点双暴力和合并,并查集维护. 本来是想再练一练 LCT 码力的,结果又调了半天. 大概错误都有: connect 函数的 \(fa\) 和 \(o\) 的顺序写错: 每一次把一条链合并成一个点双的时候需要把代表点的孩子删掉!!! 要把连边和 dfs 一起写

八中教室的灯加强版 【并查集】

本人水平有限,题解不到为处,请多多谅解 本蒟蒻谢谢大家观看 题目: 八中教室的灯加强版 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 79  Solved: 38[Submit][Status][Web Board] Description 八中一共有被用M条双向道路连接的N个教室(1<=N,M<=3000).为了关闭整个八中,master wen 计划每一次关闭掉一个教室.当一个教室被关闭了,所有的连接到这个教室的道路都会被关闭,而且再也不能够

hdu 5652 India and China Origins 并查集+二分

India and China Origins Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Description A long time ago there are no himalayas between India and China, the both cultures are frequently exchanged and are kept in

并查集+二分 旅行

题目描述: A国有n座城市,每座城市都十分美,这使得A国的民众们非常喜欢旅行.然而,A国的交通十分落后,这里只有m条双向的道路,并且这些道路都十分崎岖,有的甚至还是山路,只能靠步行.通过每条道路的长度.泥泞程度等因素,我们给每条道路评估一个"崎岖度",表示通过这条道路的不舒适程度. 从X城市经过若干条道路到达Y城市,我们称这次旅行的"代价"为所经过道路"崎岖度"的最大值.当然,如果从X城市到Y城市有多条路线,民众们会自觉选择"代价&qu