zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    方法的调用机制
    类的成员之二:方法
    类的成员之一:属性
    关键字static
    构造器
    this关键字
    递归方法(recursion)
    方法重载(二)
    GTID 跳过脚本
    mydumper 找不到libmysqlclient.so.20
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2959.html
Copyright © 2011-2022 走看看