zoukankan      html  css  js  c++  java
  • 【BZOJ2594】[WC2006] 水管局长数据加强版(重拾LCT)

    点此看题面

    大致题意: 定义一条路径的值为这条路径上权值最大值。现给定一张无向图,两种操作:询问两点间路径的最小值;删去一条边。

    前言

    (Jan 29th)刷题计划(1/6),算法标签:LCT。

    这道题应该还算比较基础的(LCT)题吧。

    好久没写(LCT)了,没想到(LCT)一遍就写对了,却挂在了其他地方......

    大致思路

    考虑如果没有删边操作,那就相当于求出原图的最小生成树,然后每次询问树上路径最大值。

    但如果要在删边的情况下维护最小生成树,是非常麻烦的。

    于是我们想到,可以离线,倒着处理,就变成了加边操作。

    那么每次只要找到树上路径最大值,然后将其与新边的权值比较,如果新边权值较小,就将原先的边断开,再连上新边。

    所以需要(LCT)来实现这里的连边、断边操作。

    大致思路就是这么简单,可实现起来略有些复杂,可详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define M 1000000
    #define Pr pair<int,int>
    #define mp make_pair
    #define max(x,y) ((x)>(y)?(x):(y))
    #define swap(x,y) (x^=y^=x^=y) 
    using namespace std;
    int n,m,k,Qt,g[N+5],St[N+5];struct Query {int op,x,y;}q[N+5];
    struct edge
    {
    	int x,y,v,p;I edge(CI a=0,CI b=0,CI c=0,CI f=0):x(min(a,b)),y(max(a,b)),v(c),p(f){}//加边时强制x<y
    }s[M+5],del[N+5];
    I bool cmp_xy(Con edge& x,Con edge& y) {return x.x^y.x?x.x<y.x:x.y<y.y;}//按端点排序
    I bool cmp_v(Con edge& x,Con edge& y) {return x.v<y.v;}//按边权排序
    I bool cmp_p(Con edge& x,Con edge& y) {return abs(x.p)<abs(y.p);}//按读入顺序排序
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
    		Tp I void writeln(Con Ty& x) {write(x),pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    class LinkCutTree//LCT
    {
    	private:
    		#define SZ N+M
    		#define PU(x)
    		(
    			O[x].Mx=O[x].V,O[x].Mp=x,
    			O[x].Mx<O[O[x].S[0]].Mx&&(O[x].Mx=O[O[x].S[0]].Mx,O[x].Mp=O[O[x].S[0]].Mp),
    			O[x].Mx<O[O[x].S[1]].Mx&&(O[x].Mx=O[O[x].S[1]].Mx,O[x].Mp=O[O[x].S[1]].Mp)
    		)
    		#define PD(x) (O[x].R&&(Re(O[x].S[0]),Re(O[x].S[1]),O[x].R=0))
    		#define Re(x) (x)&&(O[x].R^=1,swap(O[x].S[0],O[x].S[1]))
    		#define IR(x) (O[O[x].F].S[0]^(x)&&O[O[x].F].S[1]^(x))
    		#define Wh(x) (O[O[x].F].S[1]==(x))
    		#define Co(x,y,d) (O[O[x].F=y].S[d]=x)
    		#define MR(x) (Ac(x),S(x),Re(x))
    		#define Sp(x,y) (MR(x),Ac(y),S(y))
    		int St[SZ+5];struct node {int V,Mx,Mp,R,F,S[2];}O[SZ+5];
    		I void Ro(int x)
    		{
    			RI f=O[x].F,p=O[f].F,d=Wh(x);!IR(f)&&(O[p].S[Wh(f)]=x),
    			O[x].F=p,Co(O[x].S[d^1],f,d),Co(f,x,d^1),PU(f),PU(x);
    		}
    		I void S(int x)
    		{
    			RI f=x,T=0;W(St[++T]=f,!IR(f)) f=O[f].F;W(T) PD(St[T]),--T;
    			W(!IR(x)) f=O[x].F,!IR(f)&&(Ro(Wh(x)^Wh(f)?x:f),0),Ro(x);
    		}
    		I void Ac(int x) {for(RI y=0;x;x=O[y=x].F) S(x),O[x].S[1]=y,PU(x);}
    		I int FR(int x) {Ac(x),S(x);W(O[x].S[0]) PD(x),x=O[x].S[0];return S(x),x;}
    		I void Link(CI x,CI y) {MR(x),FR(y)^x&&(O[x].F=y);}
    		I void Cut(CI x,CI y) {MR(x),FR(y)==x&&O[y].F==x&&!O[y].S[0]&&(O[x].S[1]=O[y].F=0,PU(x));}
    	public:
    		I void Init() {for(RI i=1;i<=n;++i) O[i].V=-1;for(RI i=1;i<=m;++i) O[n+i].V=s[i].v;}//初始化点权
    		I void Add(CI x,CI y,CI id) {Link(x,n+id),Link(n+id,y);}//初始连边
    		I void Try(CI id)//加边操作
    		{
    			RI x=s[id].x,y=s[id].y,v=s[id].v;if(Sp(x,y),O[y].Mx<s[id].v) return;//如果路径上原最大值比新边权值小,不操作
    			RI t=O[y].Mp;Cut(s[t-n].x,t),Cut(t,s[t-n].y),Link(x,n+id),Link(n+id,y);//断掉原先的边,连上新边
    		}
    		I int Qry(CI x,CI y) {return Sp(x,y),O[y].Mx;}//求树上路径最大值
    }S;
    namespace MST//求出初始状态下的最小生成树
    {
    	int fa[N+5];I int getfa(CI x) {return fa[x]?fa[x]=getfa(fa[x]):x;}//并查集
    	I void Work()
    	{
    		RI i,fx,fy;for(i=1;i<=m;++i) s[i].p>0&&//s[i].p<0表示这条边被删掉过
    			(fx=getfa(s[i].x))^(fy=getfa(s[i].y))&&(S.Add(s[i].x,s[i].y,s[i].p),fa[fx]=fy);//最小生成树
    	}
    }
    int main()
    {
    	RI i,j,x,y,v,T=0;using namespace MST;
    	for(F.read(n,m,Qt),i=1;i<=m;++i) F.read(x,y,v),s[i]=edge(x,y,v,i);S.Init();//读入图,初始化LCT
    	for(i=1;i<=Qt;++i) F.read(q[i].op,q[i].x,q[i].y),//读入询问
    		q[i].op==2&&(del[++k]=edge(q[i].x,q[i].y,0,i),0);//对于删边操作,存储下被删的边
    	for(sort(s+1,s+m+1,cmp_xy),sort(del+1,del+k+1,cmp_xy),i=j=1;i<=m;++i)//将两组边都按端点排序
    		s[i].x==del[j].x&&s[i].y==del[j].y&&(g[del[j++].p]=s[i].p,s[i].p*=-1);//将删去的边对应到图上,并用s[i].p*=-1表示这条边被删过
    	sort(s+1,s+m+1,cmp_v),Work(),sort(s+1,s+m+1,cmp_p);//将边按权值排序后求最小生成树,然后按读入顺序排序方便之后加边
    	for(i=Qt;i;--i) q[i].op==1?St[++T]=S.Qry(q[i].x,q[i].y):(S.Try(g[i]),0);//倒着处理询问,用栈存储答案
    	W(T) F.writeln(St[T--]);return F.clear(),0;
    }
    
  • 相关阅读:
    【创建型模式】《大话设计模式》——读后感 (5)雷锋依然在人间?——工厂方法模式
    【结构型模式】《大话设计模式》——读后感 (4)为别人做嫁衣?——动态代理模式(2)
    【结构型模式】《大话设计模式》——读后感 (4)为别人做嫁衣?——静态代理模式(1)
    【结构型模式】《大话设计模式》——读后感 (3)穿什么有这么重要?——装饰模式之理解实例(2)
    【结构型模式】《大话设计模式》——读后感 (3)穿什么有这么重要?——装饰模式之理论实例(1)
    【行为型模式】《大话设计模式》——读后感 (2)商场促销?——策略模式
    【创建型模式】《大话设计模式》——读后感 (1)代码无错就是优?——简单工厂模式
    大话设计模式铺垫
    常用命令
    java简单使用netty
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2594.html
Copyright © 2011-2022 走看看