[UOJ#274][清华集训2016]温暖会指引我们前行

[UOJ#274][清华集训2016]温暖会指引我们前行

试题描述

寒冬又一次肆虐了北国大地

无情的北风穿透了人们御寒的衣物

可怜虫们在冬夜中发出无助的哀嚎

“冻死宝宝了!”

这时

远处的天边出现了一位火焰之神

“我将赐予你们温暖和希望!”

只见他的身体中喷射出火焰之力

通过坚固的钢铁,传遍了千家万户

这时,只听见人们欢呼

“暖气来啦!”

任务描述

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

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

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

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

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

输入

第一行两个正整数 n,m。表示小R的宿舍楼中有 n 个地点,共发生了 m 个事件。

接下来 m 行,每行描述一个事件,事件分为三类。

  1. find id u v t l 表示小R发现了一条连接uv之间的路,编号为id。相同id的边只会出现一次。

  2. move u v 表示小R要从u到达v,你需要计算出最温暖的路径的长度 ,若不能从u到达v,则输出1

  3. change id l 表示从uv这条边的长度变为了l(保证在当前时间点这条边存在)。

输出

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

输入示例

8 19
find 0 0 2 7 2
find 1 2 4 4 4
find 2 4 6 10 1
find 3 6 7 8 6
move 2 7
move 1 6
find 4 2 5 3 4
move 0 5
change 0 12
find 5 4 5 5 10
find 6 2 3 6 9
move 3 5
find 7 0 1 12 1
move 1 6
find 8 1 7 11 100
move 1 6
move 3 7
move 5 6
move 2 2

输出示例

11
-1
6
23
18
106
122
11
0

数据规模及约定

对于find操作:(0id<m,0u,v<n,uv,0t1000000000,0l10000)

对于move操作:(0u,v<n)

对于change操作:(0l10000)

对于100%的数据,1n100000,1m300000

题解

维护最大生成树,裸的 LCT。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 100010
#define maxq 300010
#define maxnode 400010
#define oo 2147483647

struct Node {
	int t, l, mnid, sum;
	bool rev;
	Node(): rev(0) {}
	Node(int t, int l): t(t), l(l) {}
} ns[maxnode];
int fa[maxnode], ch[maxnode][2], S[maxnode], top;
bool isrt(int u) { return !fa[u] || (ch[fa[u]][0] != u && ch[fa[u]][1] != u); }
void maintain(int o) {
	ns[o].mnid = o; ns[o].sum = ns[o].l;
	for(int i = 0; i < 2; i++) if(ch[o][i]) {
		int son = ns[ch[o][i]].mnid, &me = ns[o].mnid;
		if(ns[son].t < ns[me].t) me = son;
		ns[o].sum += ns[ch[o][i]].sum;
	}
	return ;
}
void pushdown(int o) {
	if(!ns[o].rev) return ;
	swap(ch[o][0], ch[o][1]);
	for(int i = 0; i < 2; i++) if(ch[o][i])
		ns[ch[o][i]].rev ^= 1;
	ns[o].rev = 0;
	return ;
}
void rotate(int u) {
	int y = fa[u], z = fa[y], l = 0, r = 1;
	if(!isrt(y)) ch[z][ch[z][1]==y] = u;
	if(ch[y][1] == u) swap(l, r);
	fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
	ch[y][l] = ch[u][r]; ch[u][r] = y;
	maintain(y); maintain(u);
	return ;
}
void splay(int u) {
	int t = u; S[top = 1] = t;
	while(!isrt(t)) S[++top] = fa[t], t = fa[t];
	while(top) pushdown(S[top--]);
	while(!isrt(u)) {
		int y = fa[u], z = fa[y];
		if(!isrt(y)) {
			if(ch[y][0] == u ^ ch[z][0] == y) rotate(u);
			else rotate(y);
		}
		rotate(u);
	}
	return ;
}
void access(int u) {
	splay(u); ch[u][1] = 0; maintain(u);
	while(fa[u]) splay(fa[u]), ch[fa[u]][1] = u, maintain(fa[u]), splay(u);
	return ;
}
void makeroot(int u) {
	access(u); ns[u].rev ^= 1;
	return ;
}
void link(int a, int b) {
	makeroot(b); fa[b] = a;
	return ;
}
void cut(int a, int b) {
	makeroot(a); access(b); ch[b][0] = fa[a] = 0; maintain(b);
	return ;
}
int _mnid, _sum;
void query(int a, int b) {
	makeroot(a); access(b);
	_mnid = ns[b].mnid; _sum = ns[b].sum;
	return ;
}

struct Edge {
	int u, v;
	Edge() {}
	Edge(int _, int __): u(_), v(__) {}
} es[maxq];

int pa[maxn];
int findset(int x) { return x == pa[x] ? x : pa[x] = findset(pa[x]); }

int main() {
	int n = read(), q = read();
	for(int i = 1; i <= n; i++) ns[i] = Node(oo, 0), pa[i] = i;
	char cmd[10];
	while(q--) {
		scanf("%s", cmd);
		if(!strcmp(cmd, "find")) {
			int id = read() + n + 1, u = read() + 1, v = read() + 1, t = read(), l = read();
			ns[id] = Node(t, l); es[id-n-1] = Edge(u, v);
			int U = findset(u), V = findset(v);
			if(U == V) {
				query(u, v);
				if(ns[_mnid].t < t) {
					cut(es[_mnid-n-1].u, _mnid);
					cut(_mnid, es[_mnid-n-1].v);
					link(u, id); link(id, v);
				}
			}
			else {
				pa[V] = U;
				link(u, id); link(id, v);
			}
		}
		if(!strcmp(cmd, "move")) {
			int u = read() + 1, v = read() + 1;
			if(findset(u) == findset(v)) query(u, v), printf("%d\n", _sum);
			else puts("-1");
		}
		if(!strcmp(cmd, "change")) {
			int id = read() + n + 1, l = read();
			access(id); ns[id].l = l; maintain(id);
		}
	}
	
	return 0;
}

 

转载于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6687344.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值