Luogu4219 BJOI2014 大融合 LCT

传送门

题意:写一个数据结构,支持图上连边(保证图是森林)和询问一条边两端的连通块大小的乘积。$\text{点数、询问数} \leq 10^5$



图上连边,$LCT$跑不掉

支持子树$size$有点麻烦。我们需要虚子树的$size$和(实子树的可以直接$pushup$),那么我们对于每一个点就去维护其虚子树的$size$和,那么每一个点的子树和就是可以维护的了。可以知道只有$link$和$access$操作会修改虚子树和(其他都在实链上进行操作),稍微加一点东西就行了。相对来说还是比较裸。

注意$pushup$的改变。

  1 #include<bits/stdc++.h>
  2 //This code is written by Itst
  3 using namespace std;
  4
  5 inline int read(){
  6     int a = 0;
  7     bool f = 0;
  8     char c = getchar();
  9     while(c != EOF && !isdigit(c)){
 10         if(c == ‘-‘)
 11             f = 1;
 12         c = getchar();
 13     }
 14     while(c != EOF && isdigit(c)){
 15         a = (a << 3) + (a << 1) + (c ^ ‘0‘);
 16         c = getchar();
 17     }
 18     return f ? -a : a;
 19 }
 20
 21 const int MAXN = 100010;
 22 struct node{
 23     int size , ch[2] , fa , allSize;
 24     bool mark;
 25 }Tree[MAXN];
 26 int N , M;
 27
 28 inline bool nroot(int x){
 29     return Tree[Tree[x].fa].ch[0] == x || Tree[Tree[x].fa].ch[1] == x;
 30 }
 31
 32 inline bool son(int x){
 33     return Tree[Tree[x].fa].ch[1] == x;
 34 }
 35
 36 inline void pushup(int x){
 37     Tree[x].allSize = Tree[Tree[x].ch[0]].allSize + Tree[Tree[x].ch[1]].allSize + Tree[x].size + 1;
 38 }
 39
 40 inline void ZigZag(int x){
 41     bool f = son(x);
 42     int y = Tree[x].fa , z = Tree[y].fa , w = Tree[x].ch[f ^ 1];
 43     if(nroot(y))
 44         Tree[z].ch[son(y)] = x;
 45     Tree[x].fa = z;
 46     Tree[x].ch[f ^ 1] = y;
 47     Tree[y].fa = x;
 48     Tree[y].ch[f] = w;
 49     if(w)
 50         Tree[w].fa = y;
 51     pushup(y);
 52     pushup(x);
 53 }
 54
 55 inline void pushdown(int x){
 56     if(Tree[x].mark){
 57         Tree[Tree[x].ch[0]].mark ^= 1;
 58         Tree[Tree[x].ch[1]].mark ^= 1;
 59         Tree[x].mark = 0;
 60         swap(Tree[x].ch[0] , Tree[x].ch[1]);
 61     }
 62 }
 63
 64 void pushdown_all(int x){
 65     if(nroot(x))
 66         pushdown_all(Tree[x].fa);
 67     pushdown(x);
 68 }
 69
 70 inline void Splay(int x){
 71     pushdown_all(x);
 72     while(nroot(x)){
 73         if(nroot(Tree[x].fa))
 74             ZigZag(son(x) == son(Tree[x].fa) ? Tree[x].fa : x);
 75         ZigZag(x);
 76     }
 77 }
 78
 79 inline void access(int x){
 80     for(int y = 0 ; x ; y = x , x = Tree[x].fa){
 81         Splay(x);
 82         Tree[x].size = Tree[x].size + Tree[Tree[x].ch[1]].allSize - Tree[y].allSize;
 83         Tree[x].ch[1] = y;
 84         pushup(x);
 85     }
 86 }
 87
 88 inline void makeroot(int x){
 89     access(x);
 90     Splay(x);
 91     Tree[x].mark ^= 1;
 92 }
 93
 94 inline void split(int x , int y){
 95     makeroot(x);
 96     access(y);
 97     Splay(y);
 98 }
 99
100 inline void link(int x , int y){
101     split(x , y);
102     Tree[x].fa = y;
103     Tree[y].size += Tree[x].allSize;
104     pushup(y);
105 }
106
107 inline char getc(){
108     char c = getchar();
109     while(!isupper(c))
110         c = getchar();
111     return c;
112 }
113
114 int main(){
115 #ifndef ONLINE_JUDGE
116     freopen("4219.in" , "r" , stdin);
117     //freopen("4219.out" , "w" , stdout);
118 #endif
119     N = read();
120     for(int i = 1 ; i <= N ; ++i)
121         Tree[i].allSize = 1;
122     for(M = read() ; M ; --M)
123         if(getc() == ‘A‘)
124             link(read() , read());
125         else{
126             int a = read() , b = read();
127             split(a , b);
128             printf("%lld\n" , 1ll * Tree[a].allSize * (Tree[b].allSize - Tree[a].allSize));
129         }
130     return 0;
131 }

原文地址:https://www.cnblogs.com/Itst/p/10041385.html

时间: 2024-08-11 00:39:25

Luogu4219 BJOI2014 大融合 LCT的相关文章

【bzoj4530】[Bjoi2014]大融合 LCT维护子树信息

题目描述 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量. 例如,在上图中,现在一共有了5条边.其中,(3,8)这条边的负载是6,因为有六条简单路径2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路过了(3,8). 现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的询问. 输入 第一行包含两个整数N,Q,表示星球的

[BJOI2014]大融合 LCT维护子树信息

Code: #include <cstdio> #include <algorithm> #include <cstring> #include <string> using namespace std; void setIO(string a){freopen((a+".in").c_str(),"r",stdin);} #define maxn 100009 #define ll long long int n,q

LuoguP4219 [BJOI2014]大融合(LCT)

早上考试想用\(LCT\)维护联通块\(size\),现在才发现\(LCT\)的\(size\)有虚实之分 \(Link\)与\(Acess\)中虚实变,干他丫的 \(Splay\)中只是相对关系,没有虚实变,因此不搞它 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define R(a,b,c) fo

Luogu4219 [BJOI2014]大融合

Link Solution 求两端点的子树大小然后相乘. 可以想到直接断边然后两边都\(makeroot\)一下.答案就是根节点的\(size\). 但是怎么维护\(size\)呢?实子树大小可以直接由两个实儿子得到.但是虚子树不行.所以可以对每个点多维护一个\(sv[]\),表示这个节点的虚子树大小总和. 那么\(pushup\)就相应变成: inline void pushup(int x){s[x]=s[ch[x][0]]+s[ch[x][1]]+sv[x]+1;} 我们假设已经知道每个节

P4219 [BJOI2014]大融合(LCT)

P4219 [BJOI2014]大融合 对于每个询问$(u,v)$所求的是 ($u$的虚边子树大小+1)*($v$的虚边子树大小+1) 于是我们再开个$si[i]$数组表示$i$的虚边子树大小,维护一下就好辣 #include<iostream> #include<cstdio> #include<cstring> using namespace std; inline void Swap(int &a,int &b){a^=b^=a^=b;} void

BZOJ:4530: [Bjoi2014]大融合

4530: [Bjoi2014]大融合 拿这题作为lct子树查询的练手.本来以为这会是一个大知识点,结果好像只是一个小技巧? 多维护一个虚边连接着的子树大小即可. #include<cstdio> #include<cstring> #include<algorithm> #define MN 210010 using namespace std; int p,ca,f; inline int read(){ p=0;ca=getchar();f=1; while(ca

洛谷P4219 - [BJOI2014]大融合

Portal Description 初始有\(n(n\leq10^5)\)个孤立的点,进行\(Q(Q\leq10^5)\)次操作: 连接边\((u,v)\),保证\(u,v\)不连通. 询问有多少条简单路径经过边\((u,v)\). Solution 加边用lct,询问结果相当于\(p\)为根时的\((siz[p]-siz[q])\times siz[q]\). 那么如何用lct维护子树大小呢?维护\(isiz[p]\)表示\(p\)在lct上的虚子树大小,\(siz[p]\)表示\(isiz

BJOI2014 大融合

3766. [BJOI2014]大融合 (Standard IO) Time Limits: 1000 ms  Memory Limits: 262144 KB Description 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树.这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量. 例如,在上图中,现在一共有了5条边.其中,(3,8)这条边的负载是6,因为有六条简单路径2-3-8,2-3-8-7,3-8

洛谷P4219 [BJOI2014]大融合(LCT,Splay)

LCT维护子树信息的思路总结与其它问题详见我的LCT总结 思路分析 动态连边,LCT题目跑不了了.然而这题又有点奇特的地方. 我们分析一下,查询操作就是要让我们求出砍断这条边后,x和y各自子树大小的乘积. 掌握了LCT如何维护虚子树信息和后,做法就很清晰了.split(x,y)后,输出x的虚子树和+1与y的虚子树和+1的乘积:或者,(以y为根)输出x的子树总和与y的子树总和减去x的子树总和的乘积. 代码如下(这次我试着写了一个单旋"Spaly",好像常数还小不少......) #inc