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);
		}
	}
}


發佈了44 篇原創文章 · 獲贊 11 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章