LOJ558 我们的 CPU 遭到攻击 LCT

本文介绍了一种利用LCT(Link-Cut Tree)和Splay Tree的数据结构解决森林上连边、删边、翻转点颜色及查询特定树上所有黑色点到根距离和的问题。通过化边为点,维护子树边权总和和黑点数量,实现高效查询。

传送门

题意:写一个数据结构,支持森林上:连边、删边、翻转点的颜色(黑白)、查询以某一点为根的某棵树上所有黑色点到根的距离和。$\text{点数} \leq 10^5 , \text{操作数} \leq 3 \times 10^5$


连边删边不用说就是$LCT$,而边有边权,显然需要化边为点进行操作

考虑如何维护最后一个信息。考虑$LCT$中$Splay$上的某一棵子树,$Splay$的根为$x$,其左子树为$lson$,右子树为$rson$,虚子树统一称为$son$

如果我们已经计算好了$lson,rson,son$的答案,如何计算$x$的答案?

因为$Splay$上对应原树的一条链,所以:

①$rson$与$son$中的黑点在需要经过左子树中的所有边和$x$(如果$x$代表一条边),所以答案需要加上$rson$与$son$中的黑点总数$\times\,$($x$代表的边的边权$+lson$中的实链边权总和)

②如果$x$代表一个黑点,它需要经过左子树中的所有边,所以答案需要加上$lson$中的实链边权总和

③答案再加上$lson,rson,son$的答案即可。

所以我们需要维护子树的边权总和、黑点总数,才能够计算答案。

需要注意以下几点:

①边权总和不需要将虚子树的计算在内(显然虚子树内的点都在虚子树上传的时候计算完了答案,所以在其他点上不会产生贡献),只需要把实链的计算上去

②因为需要$makeroot$操作,所以你还要记录将左右子树反过来之后的答案,这样才能正确下传标记

③注意一点:翻转左右子树对虚子树内部答案没有影响,所以虚子树答案直接上传不经过翻转的即可

 

  1 #include<bits/stdc++.h>
  2 #define lch Tree[x].ch[0]
  3 #define rch Tree[x].ch[1]
  4 #define int long long
  5 //This code is written by Itst
  6 using namespace std;
  7 
  8 inline int read(){
  9     int a = 0;
 10     bool f = 0;
 11     char c = getchar();
 12     while(c != EOF && !isdigit(c)){
 13         if(c == '-')
 14             f = 1;
 15         c = getchar();
 16     }
 17     while(c != EOF && isdigit(c)){
 18         a = (a << 3) + (a << 1) + (c ^ '0');
 19         c = getchar();
 20     }
 21     return f ? -a : a;
 22 }
 23 
 24 const int MAXN = 100010;
 25 struct node{
 26     int fa , ch[2] , size , non_size;
 27     long long val , sumE , sumL , non_sum , sumR;
 28     bool mark , col;
 29 }Tree[MAXN << 2];
 30 int N , M , K , cntNode;
 31 
 32 inline bool nroot(int x){
 33     return Tree[Tree[x].fa].ch[0] == x || Tree[Tree[x].fa].ch[1] == x;
 34 }
 35 
 36 inline bool son(int x){
 37     return Tree[Tree[x].fa].ch[1] == x;
 38 }
 39 
 40 inline void pushup(int x){
 41     Tree[x].size = Tree[lch].size + Tree[rch].size + Tree[x].non_size + Tree[x].col;
 42     Tree[x].sumE = Tree[lch].sumE + Tree[rch].sumE + Tree[x].val;
 43     Tree[x].sumL = Tree[lch].sumL + Tree[rch].sumL + Tree[x].non_sum +(Tree[rch].size + Tree[x].non_size + Tree[x].col) * (Tree[x].val + Tree[lch].sumE);
 44     Tree[x].sumR = Tree[lch].sumR + Tree[rch].sumR + Tree[x].non_sum + (Tree[lch].size + Tree[x].non_size + Tree[x].col) * (Tree[x].val + Tree[rch].sumE);
 45 }
 46 
 47 inline void rotate(int x){
 48     bool f = son(x);
 49     int y = Tree[x].fa , z = Tree[y].fa , w = Tree[x].ch[f ^ 1];
 50     if(nroot(y))
 51         Tree[z].ch[son(y)] = x;
 52     Tree[x].fa = z;
 53     Tree[x].ch[f ^ 1] = y;
 54     Tree[y].fa = x;
 55     Tree[y].ch[f] = w;
 56     if(w)
 57         Tree[w].fa = y;
 58     pushup(y);
 59 }
 60 
 61 inline void reverse(int x){
 62     Tree[x].mark ^= 1;
 63     swap(lch , rch);
 64     swap(Tree[x].sumL , Tree[x].sumR);
 65 }
 66 
 67 inline void pushdown(int x){
 68     if(Tree[x].mark){
 69         reverse(lch);
 70         reverse(rch);
 71         Tree[x].mark = 0;
 72     }
 73 }
 74 
 75 void pushdown_all(int x){
 76     if(nroot(x))
 77         pushdown_all(Tree[x].fa);
 78     pushdown(x);
 79 }
 80 
 81 inline void Splay(int x){
 82     pushdown_all(x);
 83     while(nroot(x)){
 84         if(nroot(Tree[x].fa))
 85             rotate(son(Tree[x].fa) == son(x) ? Tree[x].fa : x);
 86         rotate(x);
 87     }
 88     pushup(x);
 89 }
 90 
 91 inline void access(int x){
 92     for(int y = 0 ; x ; y = x , x = Tree[x].fa){
 93         Splay(x);
 94         Tree[x].non_size = Tree[x].non_size + Tree[Tree[x].ch[1]].size - Tree[y].size;
 95         Tree[x].non_sum = Tree[x].non_sum + Tree[Tree[x].ch[1]].sumL - Tree[y].sumL;
 96         Tree[x].ch[1] = y;
 97         pushup(x);
 98     }
 99 }
100 
101 inline void makeroot(int x){
102     access(x);
103     Splay(x);
104     reverse(x);
105 }
106 
107 inline void split(int x , int y){
108     makeroot(x);
109     access(y);
110     Splay(y);
111 }
112 
113 inline void _link(int x , int y){
114     makeroot(x);
115     makeroot(y);
116     Tree[y].non_size += Tree[x].size;
117     Tree[y].non_sum += Tree[x].sumL;
118     Tree[x].fa = y;
119     pushup(y);
120 }
121 
122 inline void link(int x , int y , int val){
123     Tree[++cntNode].val = val;
124     _link(x , cntNode);
125     _link(y , cntNode);
126 }
127 
128 inline void cut(int x , int y){
129     split(y , x);
130     if(Tree[y].fa == x){
131         Tree[y].ch[1] = 0;
132         pushup(y);
133     }
134     else
135         Tree[y].fa = Tree[Tree[y].fa].ch[0] = 0;
136     lch = Tree[lch].fa = 0;
137     pushup(x);
138 }
139 
140 inline void query(int x){
141     makeroot(x);
142     printf("%lld\n" , Tree[x].sumL);
143 }
144 
145 inline char getc(){
146     char c = getchar();
147     while(!isupper(c))
148         c = getchar();
149     return c;
150 }
151 
152 signed main(){
153 #ifndef ONLINE_JUDGE
154     freopen("558.in" , "r" , stdin);
155     freopen("558.out" , "w" , stdout);
156 #endif
157     N = read();
158     M = read();
159     K = read();
160     cntNode = N;
161     int a , b , w;
162     while(M--){
163         a = read();
164         b = read();
165         w = read();
166         link(a , b , w);
167     }
168     while(K--)
169         switch(getc()){
170         case 'L':
171             a = read();
172             b = read();
173             w = read();
174             link(a , b , w);
175             break;
176         case 'C':
177             a = read();
178             b = read();
179             cut(a , b);
180             break;
181         case 'F':
182             a = read();
183             makeroot(a);
184             Tree[a].col ^= 1;
185             pushup(a);
186             break;
187         case 'Q':
188             query(read());
189             break;
190         }
191     return 0;
192 }

 

转载于:https://www.cnblogs.com/Itst/p/10046177.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值