BZOJ 2959 长跑 LCT+动态边双

题意:

  支持加入边,修改点权,询问两点间走一条路径的最大点权和。不一定是树。

分析:

  这个题我们需要知道,假如两点间有环,那么无论如何,这个环上所有的价值都可以被我们获得。

  有环,还有这个性质,我们可以自然而然的想到边双联通分量缩点,但是我们不能对于每次加边就重构一下图,更不能每次询问都求一遍最大路,所以我们就需要一个灵活的LCT来维护我们的操作。

  我们用并查集辅助,用一个并查集维护连通性,再用一个并查集维护每个原始点属于哪个新的边双联通分量。

  当我们连接一条边时,如果两个点不连通,那么我们直接将其连通。

  如果本身已经连通,那么我们就在LCT上把这两个点之间的链搞到一个splay上,然后dfs一遍,把链上所有的点都弄进一个双连通分量里(就是用并查集并起来),继而把这些点从splay上删掉,只留一个根节点,将整个环的价值赋给它。

  修改点权的时候,我们直接把原始点的权值修改,然后算出所在连通分量的总权值应该怎样变化即可。

  至此,回答询问也就是自然而然的事情了。

代码:

 

 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=150005;
 7 int t[N][2],rev[N],val[N],sm[N],fu[N];
 8 int s[N],tp,n,m,k,fa[N],Fa[N],q,v[N];
 9 //并查集----------------------------------------↓
10 int get(int x){
11     return x==fa[x]?x:fa[x]=get(fa[x]);
12 } int Get(int x){
13     return x==Fa[x]?x:Fa[x]=Get(Fa[x]);
14 }
15 //并查集----------------------------------------↑
16 //LCT-------------------------------------------↓
17 void pushup(int x){
18     sm[x]=sm[lc(x)]+sm[rc(x)]+val[x];
19 } void revers(int x){
20     rev[x]^=1;swap(lc(x),rc(x));
21 } void pushdown(int x){
22     if(rev[x]){
23         if(lc(x)) revers(lc(x));
24         if(rc(x)) revers(rc(x));
25     } rev[x]=0;return ;
26 } bool pdrt(int x){
27     return lc(get(fu[x]))!=x&&rc(get(fu[x]))!=x; 
28 } void rotate(int x){
29     int y=get(fu[x]);int z=get(fu[y]);
30     int dy=(rc(y)==x),dz=(rc(z)==y);
31     if(!pdrt(y)) t[z][dz]=x;
32     t[y][dy]=t[x][dy^1];fu[t[y][dy]]=y;
33     t[x][dy^1]=y;fu[y]=x;fu[x]=z;pushup(y);
34 } void splay(int x){
35     s[++tp]=x;
36     for(int i=x;!pdrt(i);i=fu[i])
37     s[++tp]=get(fu[i]);
38     while(tp) pushdown(s[tp--]);
39     while(!pdrt(x)){
40         int y=get(fu[x]);int z=get(fu[y]);
41         if(!pdrt(y))
42         if(rc(y)==x^rc(z)==y) rotate(x);
43         else rotate(y);rotate(x);
44     } pushup(x);
45 } void access(int x){
46     for(int i=0;x;x=get(fu[x]))
47     splay(x),rc(x)=i,i=x;
48 } void mkrt(int x){
49     access(x);splay(x);revers(x);
50 } int fdrt(int x){
51     access(x),splay(x);
52     while(lc(x)) pushdown(x),x=lc(x);
53     return x;
54 } void split(int x,int y){
55     mkrt(x);access(y);splay(y);
56 } void link(int x,int y){
57     mkrt(x);if(fdrt(y)!=x) fu[x]=y;
58 } void cut(int x,int y){
59     mkrt(x);
60     if(fdrt(y)==x&&fu[y]==x&&!rc(x))
61     fu[x]=t[y][0]=0;pushup(y);
62 } 
63 //LCT-------------------------------------------↑
64 //DFS-------------------------------------------↓
65 void dfs(int x,int y){
66     fa[x]=y;pushdown(x);
67     if(lc(x)) dfs(lc(x),y);
68     if(rc(x)) dfs(rc(x),y);
69 } 
70 //DFS-------------------------------------------↑
71 int main(){
72     scanf("%d%d",&n,&m);
73     for(int i=1;i<=n;i++)
74     scanf("%d",&v[i]),Fa[i]=i,
75     val[i]=s[i]=v[i],fa[i]=i;
76     for(int i=1,op,a,b;i<=m;i++){
77         scanf("%d%d%d",&op,&a,&b);
78         if(op==1){
79             a=get(a),b=get(b);
80             if(a==b) continue;
81             split(a,b);
82             if(Get(a)!=Get(b))
83             fu[a]=b,Fa[Fa[a]]=Fa[b];
84             else val[b]=sm[b],dfs(b,b),
85             t[b][0]=t[b][1]=0;
86         } int d;if(op==2)
87         d=a,a=get(a),splay(a),
88         val[a]+=b-v[d],v[d]=b,pushup(a); 
89         if(op==3){
90             a=get(a),b=get(b);
91             if(Get(a)!=Get(b)) puts("-1");
92             else split(a,b),printf("%d\n",sm[b]);
93         }
94     } return 0;
95 }
LCT

 

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值