洛谷P4219 [BJOI2014]大融合

题目描述

小强要在NN 个孤立的星球上建立起一套通信系统。这套通信系统就是连接NN 个点的一个树。 这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它的简单路径的数量。

例如,在上图中,现在一共有了55 条边。其中,(3,8)(3,8) 这条边的负载是66 ,因 为有六条简单路径2-3-8238 ,2-3-8-72387 ,3-8,3-8-738,387 ,4-3-8438 ,4-3-8-74387 路过了(3,8)(3,8) 。

现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的 询问。

输入输出格式

输入格式:

第一行包含两个整数 N, QN,Q ,表示星球的数量和操作的数量。星球从 11 开始编号。

接下来的 QQ 行,每行是如下两种格式之一:

  • A x y 表示在 xx 和 yy 之间连一条边。保证之前 xx 和 yy 是不联通的。
  • Q x y表示询问 (x,y)(x,y) 这条边上的负载。保证 xx 和 yy 之间有一条边。
输出格式:

对每个查询操作,输出被查询的边的负载。

输入输出样例

输入样例#1: 
8 6
A 2 3
A 3 4
A 3 8
A 8 7
A 6 5
Q 3 8
输出样例#1: 
6




说明

对于所有数据,1≤N,Q≤10^51N,Q105

LCT大法好!

维护虚树中每个节点的虚子节点个数。

连边时注意:不是 makeroot ,是 split 。(坑了我好久。。。)

还有 access 时维护一下即可。

最后输出 (x的虚子节点个数 * ( y的虚子节点个数 - x的虚子节点个数 ))

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 100010
using namespace std;
int n,m;
struct node{
	int f,v,s,flag,son[2];
}a[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline bool isroot(int rt){
	return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
}
inline void pushup(int rt){
	if(!rt)return;
	a[rt].s=a[a[rt].son[0]].s+a[a[rt].son[1]].s+a[rt].v+1;
}
inline void pushdown(int rt){
	if(!rt||!a[rt].flag)return;
	a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1;
	swap(a[rt].son[0],a[rt].son[1]);
}
inline void turn(int rt){
	int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
	if(!isroot(x)){
		if(a[y].son[0]==x)a[y].son[0]=rt;
		else a[y].son[1]=rt;
	}
	a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
	a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x;
	pushup(x);pushup(rt);
}
void splay(int rt){
	int top=0,stack[MAXN];
	stack[++top]=rt;
	for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f;
	while(top)pushdown(stack[top--]);
	while(!isroot(rt)){
		int x=a[rt].f,y=a[x].f;
		if(!isroot(x)){
			if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt);
			else turn(x);
		}
		turn(rt);
	}
}
void access(int rt){
	for(int i=0;rt;i=rt,rt=a[rt].f){
		splay(rt);
		a[rt].v+=a[a[rt].son[1]].s-a[i].s;
		a[rt].son[1]=i;
		pushup(rt);
	}
}
inline void makeroot(int rt){access(rt);splay(rt);a[rt].flag^=1;}
inline void split(int x,int y){makeroot(x);access(y);splay(y);}
inline void link(int x,int y){
    split(x,y);
	a[x].f=y;
    a[y].v+=a[x].s;
    pushup(y);
}
void work(){
	char ch[2];
	int x,y;
	n=read();m=read();
	for(int i=1;i<=n;i++)a[i].s=1;
	while(m--){
		scanf("%s",ch);x=read();y=read();
		if(ch[0]=='A')link(x,y);
		if(ch[0]=='Q'){
			split(x,y);
			printf("%lld\n",(long long)a[x].s*(a[y].s-a[x].s));
		}
	}
}
int main(){
	work(); 
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值