bzoj3123

题意:给定一个森林,每个点有权值,有2个操作,一个是求x到y路径上第k小权值是什么,此时保证xy联通且存在k小权值,第二个操作是连接x到y这条边,操作完成后还是个森林.

Input

第一行包含一个正整数testcase,表示当前测试数据的测试点编号。保证1≤testcase≤20。 
第二行包含三个整数N,M,T,分别表示节点数、初始边数、操作数。第三行包含N个非负整数表示 N个节点上的权值。 
 接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分。

Output

对于每一个第一类操作,输出一个非负整数表示答案。 


第一个操作,传统的做法,我们对于每个点建立一个可持久化线段树,代表根到这个点路径上权值的信息,在查询x和y的时候,我们求出x和y的最近公共祖先,然后在线段树上x+y-lca-father(lca)的信息就是我们要求的答案。
第二问的话我们直接暴力连边(拥有较多节点的那个点作为父亲),对于小的那个树,直接暴力修改倍增数组,需要注意的是一个点在森林中的深度变化是没有规律的,所以求倍增数组的时候要for满。
代码如下:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 100005;
int sum[MAXN * 150], lc[MAXN * 150], rc[MAXN * 150], root[MAXN];
int first[MAXN], next[MAXN << 1], go[MAXN << 1], t, sb;
int n, m, i, j, k, q[MAXN], x, y, z, T, l, r, inf;
int f[MAXN][17], ans, dep[MAXN], a[MAXN], len, g[MAXN];
int fa[MAXN], size[MAXN];
char c[10];
inline int get()
{
	char c;
	while ((c = getchar()) < 48 || c > 57);
	int res = c - 48;
	while ((c = getchar()) >= 48 && c <= 57)
	res = res * 10 + c - 48;
	return res;
}
inline int find(int x)
{
	if (fa[x] != x) fa[x] = find(fa[x]);
	return fa[x];
}
inline void add(const int &x, const int &y)
{
	next[++t] = first[x]; first[x] = t; go[t] = y;
	next[++t] = first[y]; first[y] = t; go[t] = x;
}
inline int getlca(int x, int y)
{
	if (dep[x] < dep[y]) swap(x, y);
	for(int i = g[dep[x] - dep[y]]; i >= 0 && f[x][0]; i --)
	{
		if (dep[f[x][i]] >= dep[y]) x = f[x][i];
		if (x == y) return x;
	}
	for(int i = g[dep[y]]; i >= 0 && f[x][0] != f[y][0]; i --)
		if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	return f[x][0];
}
inline void insert(int &x, int y, int p, int q, int w)
{
	x = ++len;
	sum[x] = sum[y] + 1;
	if (p == q) return;
	lc[x] = lc[y];
	rc[x] = rc[y];
	int mid = (p + q) >> 1;
	if (mid >= w) insert(lc[x], lc[y], p, mid, w);
	else insert(rc[x], rc[y], mid + 1, q, w);
}
inline int find(int x, int y, int z, int zz, int k, int p, int q)
{
	if (p == q) return p;
	int tt = sum[lc[x]] + sum[lc[y]] - sum[lc[z]] - sum[lc[zz]];
	int mid = (p + q) >> 1;
	if (tt >= k) return find(lc[x], lc[y], lc[z], lc[zz], k, p, mid);
	else return find(rc[x], rc[y], rc[z], rc[zz], k - tt, mid + 1, q);
}
inline void dfs(int now, int fat)
{
	dep[now] = dep[fat] + 1;
	for(int i = 1; i <= 16; i ++)
		f[now][i] = f[f[now][i - 1]][i - 1];
	insert(root[now], root[fat], 0, inf, a[now]);
	for(int i = first[now]; i; i = next[i])
		if (go[i] != fat)
		{
			f[go[i]][0] = now;
			dfs(go[i], now);
		}
}
int main()
{
	freopen("forest.in", "r", stdin);
	freopen("forest.out", "w", stdout);
	g[1] = 0;
	x = 2;
	for(i = 1; i <= 17; i ++)
	{
		for(j = x; j < (x << 1) && j <= 100000; j ++)
			g[j] = i;
		x <<= 1;
	}
	sb = get();
	n = get(); m = get(); T = get();
	for(i = 1; i <= n; i ++)
		a[i] = get(), inf = max(inf, a[i]), fa[i] = i, size[i] = 1;
	for(i = 1; i <= m; i ++)
	{
		x = get(); y = get();
		add(x, y);
		int xx = find(x), yy = find(y);
		if (size[xx] < size[yy]) swap(x, y), swap(xx, yy);
		fa[yy] = xx;
		size[xx] += size[yy];
	}
	for(i = 1; i <= n; i ++)
		if (!dep[i]) dfs(i, 0);
	while (T --)
	{
		scanf("%s", c);
		if (c[0] == 'Q')
		{
			x = get(); y = get(); k = get();
			x ^= ans; y ^= ans; k ^= ans;
			int lca = getlca(x, y);
			ans = find(root[x], root[y], root[lca], root[f[lca][0]], k, 0, inf);
			printf("%d\n", ans);
		}
		else
		{
			x = get(); y = get();
			x ^= ans; y ^= ans;
			int xx = find(x), yy = find(y);
			if (size[xx] < size[yy]) swap(xx, yy), swap(x, y);
			fa[yy] = xx;
			size[xx] += size[yy];
			add(x, y);
			f[y][0] = x;
			dfs(y, x);
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值