bzoj 2959: 长跑(LCT+并查集)

2959: 长跑

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 315   Solved: 178
[ Submit][ Status][ Discuss]

Description

  某校开展了同学们喜闻乐见的阳光长跑活动。为了能“为祖国健康工作五十年”,同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑运动。一时间操场上熙熙攘攘,摩肩接踵,盛况空前。
  为了让同学们更好地监督自己,学校推行了刷卡机制。
  学校中有n个地点,用1到n的整数表示,每个地点设有若干个刷卡机。
  有以下三类事件:
  1、修建了一条连接A地点和B地点的跑道。
  2、A点的刷卡机台数变为了B。
  3、进行了一次长跑。问一个同学从A出发,最后到达B最多可以刷卡多少次。具体的要求如下:
  当同学到达一个地点时,他可以在这里的每一台刷卡机上都刷卡。但每台刷卡机只能刷卡一次,即使多次到达同一地点也不能多次刷卡。
  为了安全起见,每条跑道都需要设定一个方向,这条跑道只能按照这个方向单向通行。最多的刷卡次数即为在任意设定跑道方向,按照任意路径从A地点到B地点能刷卡的最多次数。

Input

  输入的第一行包含两个正整数n,m,表示地点的个数和操作的个数。
  第二行包含n个非负整数,其中第i个数为第个地点最开始刷卡机的台数。
  接下来有m行,每行包含三个非负整数P,A,B,P为事件类型,A,B为事件的两个参数。
  最初所有地点之间都没有跑道。
  每行相邻的两个数之间均用一个空格隔开。表示地点编号的数均在1到n之间,每个地点的刷卡机台数始终不超过10000,P=1,2,3。

Output


  输出的行数等于第3类事件的个数,每行表示一个第3类事件。如果该情况下存在一种设定跑道方向的方案和路径的方案,可以到达,则输出最多可以刷卡的次数。如果A不能到达B,则输出-1。

Sample Input

9 31
10 20 30 40 50 60 70 80 90
3 1 2
1 1 3
1 1 2
1 8 9
1 2 4
1 2 5
1 4 6
1 4 7
3 1 8
3 8 8
1 8 9
3 8 8
3 7 5
3 7 3
1 4 1
3 7 5
3 7 3
1 5 7
3 6 5
3 3 6
1 2 4
1 5 5
3 3 6
2 8 180
3 8 8
2 9 190
3 9 9
2 5 150
3 3 6
2 1 210
3 3 6

Sample Output


-1
-1
80
170
180
170
190
170
250
280
280
270
370
380
580

HINT

数据规模及约定

  对于100%的数据,m<=5n,任意时刻,每个地点的刷卡机台数不超过10000。N<=1.5×105

Source

[ Submit][ Status][ Discuss]


题解:LCT+并查集

LCT维护双联通性。

这道题如果没有加边操作,那么可以用tarjan缩点然后建树链剖。但是加边的话会使原本构不成环的部分构成新的环所以考虑在树上用LCT进行动态的缩点和维护操作。

如果加入某条边之前两个端点就联通,那么我们就将两个点之间的路径提出来,加入改边会使这条路径上的点可以互相到达,所以我们把这些点缩成一个代表节点,然后将权值都加给这个节点。并用另外一个并查集维护每个点所属的代表元素信息。

对于询问2,其实就变成了从LCT中提取缩点后两点所属的点之间的路径上的权值总和。

写LCT是需要注意,以前找的是fa[x],现在应该是找findset(fa[x]),就是fa[x]所属的缩点的代表元素。

因为每个点最多只会被合并一次,所以我们的时间复杂度仍然是O(MlogN),但是常数巨大,所以能不findset就不要findset!!!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 150003
using namespace std;
int n,m,top,st[N],father[N],rev[N],a[N];
int belong[N],fa[N],ch[N][2],val[N],mx[N];
int read()
{
	char c=getchar();
	for (;c>'9'||c<'0';c=getchar());
	int num=0;
	for (;c>='0'&&c<='9';c=getchar())  
	 num=num*10+c-'0';
	return num;
}
int findset(int x)
{
	if (belong[x]==x) return x;
	belong[x]=findset(belong[x]);
	return belong[x];
}
int find(int x)
{
	if (father[x]==x) return x;
	father[x]=find(father[x]);
	return father[x];
}
int isroot(int x)
{
   int f=findset(fa[x]);
   return ch[f][1]!=x&&ch[f][0]!=x;
}
int get(int x)
{
	return ch[findset(fa[x])][1]==x;
}
void update(int x)
{
	mx[x]=val[x];
	if (ch[x][0]) mx[x]+=mx[ch[x][0]];
	if (ch[x][1]) mx[x]+=mx[ch[x][1]];
}
void pushdown(int x)
{
	if (rev[x]){
		swap(ch[x][0],ch[x][1]);
		rev[ch[x][1]]^=1; rev[ch[x][0]]^=1;
		rev[x]=0;
	}
}
void rotate(int x)
{
	int y=fa[x]; int z=fa[y]; int which=get(x);
	if (!isroot(y)) ch[z][ch[z][1]==y]=x;
	ch[y][which]=ch[x][which^1]; fa[ch[x][which^1]]=y;
	ch[x][which^1]=y; fa[y]=x; fa[x]=z;
	update(y); update(x);
}
void splay(int x)
{
	top=0; st[++top]=x;
	for (int i=x;!isroot(i);i=findset(fa[i]))
	 st[++top]=findset(fa[i]);
	for (int i=top;i>=1;i--) pushdown(st[i]),fa[st[i]]=findset(fa[st[i]]);
	int y;
	while (!isroot(x)){
		y=fa[x];
		if (!isroot(y)) rotate(get(x)==get(y)?y:x);
		rotate(x);
	}
}
void access(int x)
{
	int t=0;
	while (x){
		splay(x);
		ch[x][1]=t;
		update(x);
		t=x; x=findset(fa[x]);
	}
}
void rever(int x)
{
	access(x); splay(x); rev[x]^=1;
}
void link(int x,int y)
{
	rever(x); fa[x]=y;
}
void merge(int x,int y)
{
	belong[findset(x)]=findset(y); pushdown(x);
	if (x!=y) val[y]+=val[x];
	//fa[x]=0;
	if (ch[x][0]) merge(ch[x][0],y);
	if (ch[x][1]) merge(ch[x][1],y);
	ch[x][1]=ch[x][0]=0;
}
int main()
{
	freopen("run.in","r",stdin);
	freopen("run.out","w",stdout);
    n=read();
	m=read();
	for (int i=1;i<=n;i++){
		val[i]=read(); a[i]=val[i];
		belong[i]=i; father[i]=i;
	}
	for (int i=1;i<=m;i++) {
		int opt,x,y; opt=read(); x=read(); y=read();
		if (opt==1) {
			x=findset(x); y=findset(y);
			if (x==y) continue;
			int r1=find(x); int r2=find(y);
			if (r1!=r2) {
				father[r2]=r1;
				link(x,y);
			}
			else {
				rever(x); access(y); splay(y); 
				merge(y,y); update(y);
			}
		}
		if (opt==2) {
			int xx=x;
			x=findset(x);
			access(x); splay(x); val[x]+=y-a[xx]; a[xx]=y;
			update(x); 
		}
		if (opt==3) {
			x=findset(x); y=findset(y);
			if (find(x)!=find(y)) {
				printf("-1\n");
				continue;
			}
			rever(x); access(y); splay(y); printf("%d\n",mx[y]);
		}
	}
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值