• 【BZOJ2959】长跑(LCT)


    点此看题面

    大致题意: 给定(n)个点,支持三种操作:连一条无向边;修改一个点的权值;让你给所有边定向,求出一个点到另一个点所经点权的最大和(每个点可经过多次,但点权只计算一次)。

    前言

    (LCT)一眼题。

    果然我(LCT)就是好写,写完调出一个很(naive)的错误就过了样例,然后一交直接过了。

    大致思路

    首先考虑对于一次询问,最优方案是什么。

    然后就会发现,对于两点间路径上的一个环,我们必然可以走过环上的所有点。

    也就是说,我们可以把每个环压缩成一个点,然后就变成了询问一条树上路径的权值和。

    好,连边、单点修改、询问树上路径,当然是我(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 150000
    #define M 750000
    using namespace std;
    int n,a[N+5],f[N+5];I int fa(CI x) {return f[x]^x?f[x]=fa(f[x]):x;}
    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 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=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void NA() {pc('-'),pc('1'),pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    class LinkCutTree//LCT
    {
    	private:
    		#define IR(x) (O[fa(O[x].F)].S[0]^x&&O[fa(O[x].F)].S[1]^x)
    		#define Wh(x) (O[fa(O[x].F)].S[1]==x)
    		#define Co(x,y,d) (O[O[x].F=y].S[d]=x)
    		#define PU(x) (O[x].G=O[x].V+O[O[x].S[0]].G+O[O[x].S[1]].G)
    		#define PD(x) O[x].R&&(Re(O[x].S[0]),Re(O[x].S[1]),O[x].R=0)
    		#define Re(x) (swap(O[x].S[0],O[x].S[1]),O[x].R^=1)
    		#define MR(x) (Ac(x),S(x),Re(x))
    		int St[N+5];struct node {int G,V,R,F,S[2];}O[N+5];
    		I void Ro(RI x)
    		{
    			RI f=fa(O[x].F),p=fa(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(RI x)
    		{
    			RI f=x,T=0;W(St[++T]=f,!IR(f)) f=fa(O[f].F);W(T) PD(St[T]),--T;
    			W(!IR(x)) f=fa(O[x].F),!IR(f)&&(Ro(Wh(x)^Wh(f)?x:f),0),Ro(x);
    		}
    		I void Ac(RI x) {for(RI y=0;x;x=fa(O[y=x].F)) S(x),O[x].S[1]=y,PU(x);}
    		I int FR(RI x) {Ac(x),S(x);W(O[x].S[0]) x=O[x].S[0];return S(x),x;}
    		I void dfs(CI x,CI rt) {x&&(f[fa(x)]=rt,dfs(O[x].S[0],rt),dfs(O[x].S[1],rt),0);}//遍历子树,在并查集上合并
    		I void Merge(CI x,CI y) {dfs(x,x),O[x].V=O[x].G,O[x].S[0]=O[x].S[1]=0;}//缩点,修改点权以及子节点
    	public:
    		I void Init(CI x,int *a) {for(RI i=1;i<=x;++i) O[i].V=O[i].G=a[i];}
    		I bool Identify(CI x,CI y) {return MR(x),FR(y)==x;}//判断是否连通
    		I void Link(CI x,CI y) {Identify(x,y)?Merge(x,y),0:O[x].F=y;}//连边
    		I void U(CI x,CI v) {MR(x),O[x].V+=v,O[x].G+=v;}//单点修改权值
    		I int Q(CI x,CI y) {return MR(x),Ac(y),S(y),O[y].G;}//询问树上路径
    }LCT;
    int main()
    {
    	RI Qt,i,op,x,y;for(F.read(n,Qt),i=1;i<=n;++i) F.read(a[i]),f[i]=i;
    	LCT.Init(n,a);W(Qt--) switch(F.read(op,x,y),op)
    	{
    		case 1:LCT.Link(fa(x),fa(y));break;case 2:LCT.U(fa(x),y-a[x]),a[x]=y;break;
    		case 3:LCT.Identify(fa(x),fa(y))?F.writeln(LCT.Q(fa(x),fa(y))):F.NA();break;
    	}return F.clear(),0;
    }
    
  • 相关阅读:
    CodeForces Gym 100935G Board Game DFS
    CodeForces 493D Vasya and Chess 简单博弈
    CodeForces Gym 100935D Enormous Carpet 快速幂取模
    CodeForces Gym 100935E Pairs
    CodeForces Gym 100935C OCR (水
    CodeForces Gym 100935B Weird Cryptography
    HDU-敌兵布阵
    HDU-Minimum Inversion Number(最小逆序数)
    七月馒头
    非常可乐
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2959.html
Copyright © 2020-2023  润新知