BZOJ2594 水管局长数据加强版LCT

本文介绍了一种使用Link-Cut-Tree数据结构动态维护最小生成树的方法,以解决无向带权图中最大权最小路径的查询及边的删除操作。通过逆向处理操作和询问,将删边转化为连边,确保最小生成树的正确维护。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

  您有一个无向带权图,您需要支持两种操作。

  1. 询问两个点之间的最大权最小路径。
  2. 删除一条边。

分析:

  众所周知,任意两点间最大权最小路径存在于最小生成树上,所以这道题可以动态维护最小生成树。

  用到的数据结构是可以支持连边和断边的Link- Cut- Tree。

  用LCT维护一棵最小生成树,是不可能支持在原图上删边这一操作的,因为既不知道删除的边是否在最小生成树上,也不了解(如果删除的是最小生成树上的边)删除之后该用哪条边来保证树的连通。

  正难则反!

  我们把操作和询问都读进来,倒着处理,然后删边变成连边操作。(读进来的时候把需要被删除的边都打上标记)要在处理之前把没有标记的边都连好。

  维护最小生成树的方法是:在加一条边时,询问连通的两个点之间权值最大的边,之后与新边相比较,如果新边更优,那么将最劣的边删掉,连接新边,否则忽略新边。

  LCT维护的是链上的点权,我们要维护边权,方法是把一条边转化成“边——点——边” 的形式,直接给点权即可,普通的点权值为零!

代码:

 

  1 #include<bits/stdc++.h>
  2 #define max(a,b,c) max(max(a,b),c)
  3 #define lc(x) t[x][0]
  4 #define rc(x) t[x][1]
  5 using namespace std;
  6 const int N=1500005;
  7 struct node{int x,y,z,id;bool d;}e[N];
  8 struct query{int f,x,y,ans,id;}q[N];
  9 int t[N][2],rev[N],val[N],mx[N],fu[N];
 10 int s[N],tp=0,n,m,k,fa[N];
 11 char readchar(){
 12     static char buf[100000],*l=buf,*r=buf;
 13     if(l==r) r=(l=buf)+fread(buf,1,100000,stdin);
 14     if(l==r) return EOF;return *l++;}
 15 int read(){
 16     int x=0,f=1;char ch=readchar();
 17     while(ch<'0'||ch>'9'){if(ch=='-') f=-f;ch=readchar();}
 18     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=readchar();};
 19     return x*f;
 20 } char pbuf[100000],*pp=pbuf;
 21   void push(const char c) {
 22     if(pp-pbuf==100000) fwrite(pbuf,1,100000,stdout),pp=pbuf;
 23     *pp++=c;
 24 } void write(int x) {
 25     static int sta[35];
 26     int top=0;
 27     do{sta[top++]=x%10,x/=10;}while(x);
 28     while(top) push(sta[--top]+'0');push('\n');
 29 } bool operator< (node a,node b){
 30     return a.x==b.x?a.y<b.y:a.x<b.x;
 31 } bool cmp(node a,node b){
 32     return a.z<b.z;
 33 } bool cmp2(node a,node b){
 34     return a.id<b.id;
 35 } int get(int x){
 36     return fa[x]==x?x:fa[x]=get(fa[x]);
 37 } int find(int x,int y){
 38     int l=1,r=m;node p=(node){x,y,0,0,0};
 39     while(l<=r){
 40         int mid=l+r>>1;
 41         if(e[mid]<p) l=mid+1;
 42         else if(e[mid].x==x&&e[mid].y==y) return mid;
 43         else r=mid-1;
 44     } return 0;
 45 } void pushup(int x){
 46     int ls=lc(x),rs=rc(x);
 47     mx[x]=x;
 48     if(val[mx[ls]]>val[mx[x]]) 
 49     mx[x]=mx[ls];
 50     if(val[mx[rs]]>val[mx[x]]) 
 51     mx[x]=mx[rs];
 52 } void pushdown(int x){
 53     int ls=lc(x),rs=rc(x);
 54     if(rev[x]) rev[ls]^=1,rev[rs]^=1,
 55     swap(lc(x),rc(x)),rev[x]=0;return;
 56 } bool pdrt(int x){
 57     return lc(fu[x])!=x&&rc(fu[x])!=x;
 58 } void rotate(int x){
 59     int y=fu[x];int z=fu[y];
 60     int dy=(rc(y)==x),dz=(rc(z)==y);
 61     if(!pdrt(y)) t[z][dz]=x;
 62     t[y][dy]=t[x][dy^1],fu[t[x][dy^1]]=y;
 63     t[x][dy^1]=y;fu[y]=x;fu[x]=z;
 64     pushup(y);
 65 } void splay(int x){
 66     s[++tp]=x;
 67     for(int i=x;!pdrt(i);i=fu[i])
 68     s[++tp]=fu[i];while(tp)
 69     pushdown(s[tp--]);
 70     while(!pdrt(x)){
 71         int y=fu[x];int z=fu[y];if(!pdrt(y))
 72         if(rc(y)==x^rc(z)==y) rotate(x);
 73         else rotate(y);rotate(x);
 74     } pushup(x);
 75 } void access(int x){
 76     for(int i=0;x;x=fu[x])
 77     splay(x),rc(x)=i,i=x;
 78 } void mkrt(int x){
 79     access(x),splay(x);rev[x]^=1;
 80 } int fdrt(int x){
 81     access(x);splay(x);
 82     while(lc(x)) pushdown(x),x=lc(x);
 83     return x;
 84 } void split(int x,int y){
 85     mkrt(x);access(y);splay(y);
 86 } void link(int x,int y){
 87     mkrt(x);if(fdrt(y)!=x) fu[x]=y;
 88 } void cut(int x,int y){
 89     mkrt(x);
 90     if(fdrt(y)==x&&fu[x]==y&&!rc(x))
 91     fu[x]=t[y][0]=0;pushup(y);
 92 } int ask(int x,int y){
 93     split(x,y);return mx[y];
 94 } signed main(){
 95     n=read();m=read();k=read();
 96     for(int i=1;i<=n;fa[i]=i,i++);
 97     for(int i=1;i<=m;i++){
 98         e[i].x=read();e[i].y=read();e[i].z=read();
 99         if(e[i].x>e[i].y) swap(e[i].x,e[i].y);
100     } sort(e+1,e+1+m,cmp);
101     for(int i=1;i<=m;i++)
102     e[i].id=i,val[n+i]=e[i].z,mx[n+i]=n+i;
103     sort(e+1,e+1+m);
104     for(int i=1;i<=k;i++){
105         q[i].f=read();q[i].x=read();q[i].y=read();
106         if(q[i].f==2){
107             if(q[i].x>q[i].y) swap(q[i].x,q[i].y);
108             int p=find(q[i].x,q[i].y);
109             e[p].d=1,q[i].id=e[p].id;
110         } 
111     } sort(e+1,e+m+1,cmp2);int tot=0;
112     for(int i=1;i<=m;i++)
113     if(!e[i].d){
114         int u=e[i].x,v=e[i].y;
115         int x=get(u),y=get(v);
116         if(x!=y){
117             fa[x]=y;link(u,i+n);link(v,i+n);
118             tot++;if(tot==n-1) break;
119         }
120     } for(int i=k;i;i--)
121     if(q[i].f==1) 
122     q[i].ans=val[ask(q[i].x,q[i].y)];
123     else{
124         int x=q[i].x,y=q[i].y,o=q[i].id;
125         int p=ask(x,y);
126         if(e[o].z<val[p])
127         cut(e[p-n].x,p),cut(e[p-n].y,p),
128         link(x,o+n),link(y,o+n);
129     } for(int i=1;i<=k;i++)
130     if(q[i].f==1) write(q[i].ans);
131     fwrite(pbuf,1,pp-pbuf,stdout); 
132     return 0;
133 }
Link-Cut-Tree

 

转载于:https://www.cnblogs.com/Alan-Luo/articles/10155902.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值