您的位置:首页 > 运维架构

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

2017-12-23 23:48 651 查看

说在前面

好久没更新博客了=w=

多校集训忙的要死,也累得要死….

要学的东西好多好多好多…

大家都很强啊,要好好的努力一把了!

题目

BZOJ4530传送门

题面

小强要在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,表示星球的数量和操作的数量。星球从1开始编号。接下来的Q行,每行是如下两种格式之一:

A x y表示在x和y之间连一条边。保证之前x和y是不联通的。

Q x y表示询问(x,y)这条边上的负载。保证x和y之间有一条边。

数据范围:1≤N,Q≤100000

输出格式:

对每个查询操作,输出一行一个整数,表示被查询的边的负载。

解法

一道很入门的LCT维护子树和=w=

每个Splay节点维护两个信息,LCT(splay)树上实儿子的子树大小(包括实儿子的虚儿子)rsiz,和虚儿子的子树大小isiz。

实儿子,就是nd−>fa−>ch[0/1]==nd,

虚儿子,就是nd−>fa−>ch[0/1] != nd

每个Splay根节点的rsiz+isiz,表示的就是当前链顶在原树里的子树大小

对于题目中的操作:

连接操作就是Link操作,这时注意更新 虚子树大小

询问操作,先把其中一个点x换根,然后Access另外一个点y,那么y点的(虚子树大小+1)就是原树中y的子树大小。而x的子树大小,只需用总siz减去y的那部分即可

下面是自带大常数的代码

/**************************************************************
Problem: 4530
User: Izumihanako
Language: C++
Result: Accepted
Time:1312 ms
Memory:3556 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , Q ;
struct Node{
int rsiz , isiz ;
bool rev ;
Node *ch[2] , *fa ;
void rever(){
swap( ch[0] , ch[1] ) ;
rev ^= 1 ;
}
void pushdown(){
if( rev ){
if( ch[0] ) ch[0]->rever() ;
if( ch[1] ) ch[1]->rever() ;
rev = false ;
}
}
void update(){
rsiz = 1 ;
if( ch[0] ) rsiz += ch[0]->rsiz + ch[0]->isiz ;
if( ch[1] ) rsiz += ch[1]->rsiz + ch[1]->isiz ;
}
}w[100005] ;

bool isroot( Node *x ){
if( !x->fa ) return true ;
if( x->fa->ch[0] != x && x->fa->ch[1] != x ) return true ;
return false ;
}

void Rotate( Node *nd , int aim ){
Node *x = nd->ch[aim] ;
if( nd->fa ){
if( nd->fa->ch[0] == nd ) nd->fa->ch[0] = x ;
if( nd->fa->ch[1] == nd ) nd->fa->ch[1] = x ;
} x->fa = nd->fa ;

if( x->ch[aim^1] ){
x->ch[aim^1]->fa = nd ;
} nd->ch[aim] = x->ch[aim^1] ;

nd->fa = x , x->ch[aim^1] = nd ;
nd->update() ;
x->update() ;
}

Node *sta[100005] ;
int topp ;

void Splay( Node *nd ){
Node *tmp = nd ; topp = 0 ;
for( ; !isroot( tmp ) ; tmp = tmp->fa ) sta[++topp] = tmp ;
sta[++topp] = tmp ;
for( ; topp ; topp -- ) sta[topp]->pushdown() ;

while( !isroot( nd ) ){
Node *fa = nd->fa , *gdfa = fa->fa ;
int pn = ( fa->ch[1] == nd ) , pf ;
if( gdfa && !isroot( fa ) ){
pf = ( gdfa->ch[1] == fa ) ;
if( pf == pn ){
Rotate( gdfa , pf ) ;
Rotate( fa , pn ) ;
} else {
Rotate( fa , pn ) ;
Rotate( gdfa , pf ) ;
}
} else Rotate( fa , pn ) ;
}
}

void Access( Node *nd ){
Node *tmp = NULL ;
while( nd ){
Splay( nd ) ;
if( tmp )
nd->isiz -= tmp->isiz + tmp->rsiz ;
if( nd->ch[1] )
nd->isiz += nd->ch[1]->isiz + nd->ch[1]->rsiz ;
nd->ch[1] = tmp ;
nd->update() ;
tmp = nd ;
nd = nd->fa ;
}
}

void Makeroot( Node *x ){
Access( x ) ; Splay( x ) ;
x->rever() ;
}

void Link( Node *x , Node *y ){
Makeroot( x ) ;
Access( y ) ; Splay( y ) ;
x->fa = y ;
y->isiz += x->rsiz + x->isiz ;
}

void solve(){
char opt[5] ;
for( int i = 1 , x , y ; i <= Q ; i ++ ){
scanf( "%s%d%d" , opt , &x , &y ) ;
if( opt[0] == 'A' ){
Link( w + x , w + y ) ;
} else {
Makeroot( w + x ) ;
Access( w + y ) ;
Splay( w + x ) ;
printf( "%lld\n" , 1LL * ( w[y].isiz + 1 ) * ( w[x].rsiz + w[x].isiz - w[y].isiz - 1 ) ) ;
}
}
}

int main(){
scanf( "%d%d" , &N , &Q ) ;
for( int i = 1 ; i <= N ; i ++ ){
Node *nd = ( w + i ) ;
nd->ch[0] = nd->ch[1] = nd->fa = NULL ;
nd->rsiz = 1 , nd->isiz = 0 ;
nd->rev = false ;
}
solve() ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: