UOJ #274【清华集训2016】温暖会指引我们前行【LCT】

时间限制:2s2s
空间限制:512MB

任务描述

虽然小R住的宿舍楼早已来了暖气,但是由于某些原因,宿舍楼中的某些窗户仍然开着(例如厕所的窗户),这就使得宿舍楼中有一些路上的温度还是很低。

小R的宿舍楼中有n个地点和一些路,一条路连接了两个地点,小R可以通过这条路从其中任意一个地点到达另外一个地点。但在刚开始,小R还不熟悉宿舍楼中的任何一条路,所以他会慢慢地发现这些路,他在发现一条路时还会知道这条路的温度和长度。每条路的温度都是互不相同的。

小R需要在宿舍楼中活动,每次他都需要从一个地点到达另一个地点。小R希望每次活动时经过一条最温暖的路径,最温暖的路径的定义为,将路径上各条路的温度从小到大排序后字典序最大。即温度最低的路温度尽量高,在满足该条件的情况下,温度第二低的路温度尽量高,以此类推。小R不会经过重复的路。由于每条路的温度互不相同,因此只存在一条最温暖的路径。

对于小R的每次活动,你需要求出小R需要走过的路径总长度。如果小R通过当前发现的路不能完成这次活动,则输出 −1。

注意本题中的字典序与传统意义上的字典序定义有所不同,对于两个序列a,b(a≠b),若a是b的前缀则a的字典序较大,同时可以推出空串的字典序最大。

输入格式

第一行两个正整数 n,m。表示小R的宿舍楼中有 n 个地点,共发生了 m 个事件。
接下来 m 行,每行描述一个事件,事件分为三类。

find id u v t l 表示小R发现了一条连接u和v之间的路,编号为id。相同id的边只会出现一次。
move u v表示小R要从u到达v,你需要计算出最温暖的路径的长度 ,若不能从u到达v,则输出−1
change id l表示从u到v这条边的长度变为了l(保证在当前时间点这条边存在)。

输出格式

对于每个询问,输出一行整数,表示最温暖的路径长度。

限制与约定

对于find操作: ( 0 ≤ i d &lt; m , 0 ≤ u , v &lt; n , u ≠ v , 0 ≤ t ≤ 1000000000 , 0 ≤ l ≤ 10000 ) (0≤id&lt;m,0≤u,v&lt;n,u≠v,0≤t≤1000000000,0≤l≤10000) (0id<m,0u,v<n,u̸=v,0t1000000000,0l10000)
对于move操作: ( 0 ≤ u , v &lt; n ) (0≤u,v&lt;n) (0u,v<n)
对于change操作: ( 0 ≤ l ≤ 10000 ) (0≤l≤10000) (0l10000)
对于100%的数据, 1 ≤ n ≤ 100000 , 1 ≤ m ≤ 300000 1≤n≤100000,1≤m≤300000 1n100000,1m300000

测试点nm其它
1-2<=20<=50无特殊约定
3-5<=1000<=3000
6-10<=100000<=300000所有的find事件都在move事件之前,且没有change事件
11-14所有的find事件都在move事件之前
15-20无特殊约定

题目分析

久违的复习了一下LCT

都是很基础的LCT操作
每次加边时检查 u , v u,v u,v是否连通,若不连通则直接加边
若以连通则查找 u , v u,v u,v路径上温暖度最小的边,若小于当前边则直接替换

维护连通性最好用并查集,树上直接判连通容易T


#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long lt;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

const lt inf=2e9;
const int maxn=500010;
int n,m;
int fa[maxn],ch[maxn][2];
int mi[maxn],val[maxn],lzy[maxn];
lt dis[maxn],sum[maxn];
int st[maxn],ff[maxn];
int Eu[maxn],Ev[maxn];
char ss[20];

int isrt(int x)
{
	return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}

void push(int x)
{
	if(!lzy[x]) return;
	swap(ch[x][0],ch[x][1]);
	lzy[ch[x][0]]^=1; lzy[ch[x][1]]^=1;
	lzy[x]=0;
}

void update(int x)
{
	int lc=ch[x][0],rc=ch[x][1];
	mi[x]=x;
	mi[x]=val[mi[lc]]<val[mi[x]]?mi[lc]:mi[x];
	mi[x]=val[mi[rc]]<val[mi[x]]?mi[rc]:mi[x];
	sum[x]=sum[lc]+sum[rc]+dis[x];
}

void rotate(int x)
{
	int y=fa[x],z=fa[y];
	int d=(ch[y][0]==x);
	if(!isrt(y))
	{
		if(ch[z][0]==y) ch[z][0]=x;
		else ch[z][1]=x;
	}
	fa[y]=x; fa[ch[x][d]]=y; fa[x]=z;
	ch[y][d^1]=ch[x][d]; ch[x][d]=y;
	update(y); update(x);
}

void splay(int x)
{
	int top=0; st[++top]=x;
	for(int i=x;!isrt(i);i=fa[i])
	st[++top]=fa[i];
	while(top) push(st[top--]);
	
	while(!isrt(x))
	{
		int y=fa[x],z=fa[y];
		if(!isrt(y))
		{
			if((ch[z][0]==y)^(ch[y][0]==x)) rotate(x);
			else rotate(y);
		}
		rotate(x);
	}
}

void access(int x)
{
	int t=0;
	while(x)
	{
		splay(x);
		ch[x][1]=t;
		update(x);
		t=x; x=fa[x];
	}
}

void mkrt(int x)
{
	access(x); splay(x);
	lzy[x]^=1;
}

void match(int x,int y)
{
	mkrt(x); fa[x]=y;
}

void cut(int x,int y)
{
	mkrt(x);
	access(y); splay(y);
	fa[x]=ch[y][0]=0;
	update(y); 
}

int find(int x)
{
	if(x==ff[x]) return x;
	else return ff[x]=find(ff[x]);
}

int qmin(int x,int y)
{
	mkrt(x);
	access(y); splay(y);
	return mi[y];
}

void path()
{
	int id=read()+1+n;
	int u=read()+1,v=read()+1;
	Eu[id]=u; Ev[id]=v;
	val[id]=read(); dis[id]=read();
	
	int fu=find(u),fv=find(v);
	if(fu!=fv)
	{
		ff[fu]=fv;
		match(u,id); match(id,v);
	}
	else
	{
		int num=qmin(u,v);
		if(val[num]<val[id])
		{
			cut(Eu[num],num); cut(num,Ev[num]);
			match(u,id); match(id,v);
		}
	}
}

lt qsum(int x,int y)
{
	mkrt(x);
	access(y); splay(y);
	return sum[y];
}

lt query()
{
	int u=read()+1,v=read()+1;
	if(find(u)!=find(v)) return -1;
	else return qsum(u,v);
}

void change()
{
	int id=read()+1+n;
	mkrt(id);
	dis[id]=read();
	update(id);
}

int main()
{
	n=read();m=read();
	
	for(int i=0;i<=n;++i) 
	val[i]=inf,ff[i]=i;
	
	for(int i=1;i<=m;++i)
	{
		scanf("%s",&ss);
		if(ss[0]=='f') path();
		else if(ss[0]=='m') printf("%lld\n",query());
		else if(ss[0]=='c') change();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值