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

bzoj4530 [Bjoi2014]大融合 (LCT维护子树信息)

2017-11-27 22:07 429 查看

bzoj4530 [Bjoi2014]大融合

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=4530

题意:

小强要在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

题解:

neither_nor学长的讲解

首先为了维护子树信息,我们需要维护每个点实子树和虚子树的信息。(实子树的信息包括实儿子的虚子树的信息)

为什么?

尽管一个点x的实子树中并不全是他原树的子树(例如他实际的fa),

但如果 access(x),则 x的虚子树就是原树中的x的子树。

因此,只需要维护子树信息(实/虚),access后查询即可。

改变虚子树信息的只是 access和link

这时的,update就是 自己信息+ls信息+rs信息+虚子树信息。

对于这道题:

就是 size = ls.size+rs.size+虚子树size+1

改变虚子树时要减掉改变的,加上连上的子树信息。

ans=size[x]*(size总-size[x])

注意:

link x到y上前,要先把其中一方makeroot,

这和bzoj2002不同,x和y都可能有原树的父亲,x可能有ls,即真实的fa,现在又来个fa显然不行。

另外link后也要update,因为虚子树改变了。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100005;
int n,q;
struct node
{
int ch[2],fa,size,ss;
bool rev;
void init() {fa=ch[1]=ch[0]=ss=0;rev=0;size=1;}
}tr
;
struct  Link_Cut_Tree
{
bool isroot(int x) {return (tr[tr[x].fa].ch[0]!=x&&tr[tr[x].fa].ch[1]!=x);}
void pushdown(int x)
{
if(tr[x].rev)
{
swap(tr[x].ch[0],tr[x].ch[1]);
if(tr[x].ch[0]) tr[tr[x].ch[0]].rev^=1;
if(tr[x].ch[1]) tr[tr[x].ch[1]].rev^=1;
tr[x].rev^=1;
}
}
void push(int x)
{
if(!isroot(x)) push(tr[x].fa);
pushdown(x);
}
void update(int x)
{
int ls=tr[x].ch[0]; int rs=tr[x].ch[1];
tr[x].size=tr[x].ss+1+tr[ls].size+tr[rs].size;
}
void rotate(int x)
{
int y=tr[x].fa; int z=tr[y].fa;
int l=(tr[y].ch[0]==x)?0:1; int r=l^1;
if(!isroot(y)) {if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x;}
tr[x].fa=z;
tr[y].ch[l]=tr[x].ch[r]; tr[tr[x].ch[r]].fa=y;
tr[x].ch[r]=y; tr[y].fa=x;
update(y); update(x);
}
void splay(int x)
{
push(x);
while(!isroot(x))
{
int y=tr[x].fa; int z=tr[y].fa;
if(!isroot(y))
{
if((tr[y].ch[0]==x)^(tr[z].ch[0]==y)) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x)
{
int y=0;
for(;x;y=x,x=tr[x].fa)
{
splay(x);
tr[x].ss+=tr[tr[x].ch[1]].size;
tr[x].ch[1]=y; tr[x].ss-=tr[y].size;
update(x);
}
}
void makeroot(int x)
{
access(x); splay(x); tr[x].rev=1;
}
void link(int x,int y)
{
makeroot(x);    access(y); splay(y); //必须要makeroot[x] 因为若x不是根就有可能有原树上的fa,即ls,此时又加了一个fa,就有两个fa了。
tr[x].fa=y; tr[y].ss+=tr[x].size;
update(y);//!!!!!! 这里size也包含虚树的,所以ss改变要update
}
}LCT;
int main()
{
scanf("%d%d",&n,&q);
for(int i=0;i<=n;i++) tr[i].init(); tr[0].size=0;
while(q--)
{
char opt[5];int x,y; scanf("%s",opt); scanf("%d%d",&x,&y);
if(opt[0]=='A') LCT.link(x,y);
else
{
LCT.makeroot(x);
LCT.access(y);
LCT.splay(x);
printf("%lld\n",1LL*(tr[y].ss+1)*(tr[x].size-tr[y].ss-1));
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: