Codeforces Round #673 (Div. 2) F. Graph and Queries (并查集启发式合并&撤销)

F. Graph and Queries

time limit per test

1.5 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

You are given an undirected graph consisting of nn vertices and mm edges. Initially there is a single integer written on every vertex: the vertex ii has pipi written on it. All pipi are distinct integers from 11 to nn.

You have to process qq queries of two types:

  • 11 vv — among all vertices reachable from the vertex vv using the edges of the graph (including the vertex vv itself), find a vertex uu with the largest number pupu written on it, print pupu and replace pupu with 00;
  • 22 ii — delete the ii-th edge from the graph.

Note that, in a query of the first type, it is possible that all vertices reachable from vv have 00 written on them. In this case, uu is not explicitly defined, but since the selection of uu does not affect anything, you can choose any vertex reachable from vv and print its value (which is 00).

Input

The first line contains three integers nn, mm and qq (1≤n≤2⋅1051≤n≤2⋅105; 1≤m≤3⋅1051≤m≤3⋅105; 1≤q≤5⋅1051≤q≤5⋅105).

The second line contains nn distinct integers p1p1, p2p2, ..., pnpn, where pipi is the number initially written on vertex ii (1≤pi≤n1≤pi≤n).

Then mm lines follow, the ii-th of them contains two integers aiai and bibi (1≤ai,bi≤n1≤ai,bi≤n, ai≠biai≠bi) and means that the ii-th edge connects vertices aiai and bibi. It is guaranteed that the graph does not contain multi-edges.

Then qq lines follow, which describe the queries. Each line is given by one of the following formats:

  • 11 vv — denotes a query of the first type with a vertex vv (1≤v≤n1≤v≤n).
  • 22 ii — denotes a query of the second type with an edge ii (1≤i≤m1≤i≤m). For each query of the second type, it is guaranteed that the corresponding edge is not deleted from the graph yet.

Output

For every query of the first type, print the value of pupu written on the chosen vertex uu.

Example

input

5 4 6
1 2 5 4 3
1 2
2 3
1 3
4 5
1 1
2 1
2 3
1 1
1 2
1 2

output

5
1
2
0

题目大意:

给你一个无向图,每个点的点权都不同,多次询问操作,询问一是输出x所在连通块的最大点权并将其改为0,操作二是删除第x条边。

解法:

将建边的顺序改一下,那么所有询问中删除的边就可以通过并查集来撤销。建边的顺序是:询问以外的边优先,询问中的边顺序倒过来(因为栈先进后出)。再开一个二维的set,保存每个连通块祖先的点权,按照点权较大的排序;合并、撤销的操作与并查集一样。

Accepted code

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;

#define sc scanf
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define pir pair <int, int>
#define MK(x, y) make_pair(x, y)
#define MEM(x, b) memset(x, b, sizeof(x))
#define MPY(x, b) memcpy(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int Mod = 1e9 + 7;
const int N = 5e5 + 100;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
inline ll dpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % Mod; b >>= 1; t = (t*t) % Mod; }return r; }
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t); b >>= 1; t = (t*t); }return r; }

struct node
{
	int id, w;
	bool operator < (const node &oth) const {
		return w > oth.w;
	}
};
struct Ask
{
	int op, x;
}qi[N];
pir eg[N];
set <node> s[N];
int fa[N], sz[N], n, m, q;
int a[N];
bool vis[N];
stack <pir> st;

void Init() {
	for (int i = 1; i <= n; i++)
		fa[i] = i, sz[i] = 1;
}
int Find(int x) {
	while (x != fa[x])
		x = fa[x];
	return x;
}
void Merge(int x, int y) {
	x = Find(x);
	y = Find(y);
	if (x == y) {
		st.push({ 0, 0 });
		return;
	}
	if (sz[x] > sz[y])
		swap(x, y);
	fa[x] = y;
	st.push({ x, y });
	sz[y] += sz[x];
	for (auto it : s[x])
		s[y].insert(it);
}
void Cancle() {
	int x = st.top().first, y = st.top().second;
	st.pop();
	if (!x && !y)
		return;
	fa[x] = x;
	sz[y] -= sz[x];
	for (auto it : s[x]) {
		if (s[y].find(it) != s[y].end())
			s[y].erase(it);
	}
}

int main()
{
	cin >> n >> m >> q;
	Init();
	for (int i = 1; i <= n; i++)
		sc("%d", &a[i]), s[i].insert({ i, a[i] });
	for (int i = 1; i <= m; i++)
		sc("%d %d", &eg[i].first, &eg[i].second);
	for (int i = 1; i <= q; i++) {
		sc("%d %d", &qi[i].op, &qi[i].x);
		if (qi[i].op == 2)
			vis[qi[i].x] = true;
	}
	for (int i = 1; i <= m; i++)   // 先存询问以外的边
		if (!vis[i])
			Merge(eg[i].first, eg[i].second);
	for (int i = q; i >= 1; i--) {   // 询问中的边反着存
		if (qi[i].op == 1)
			continue;
		int u = eg[qi[i].x].first;
		int v = eg[qi[i].x].second;
		Merge(u, v);
	}                              // 以上都是改变建边的顺序

	for (int i = 1; i <= q; i++) {
		int op = qi[i].op, x = qi[i].x;
		if (op == 1) {
			int u = Find(x);
			while (!s[u].empty() && !a[s[u].begin()->id])
				s[u].erase(s[u].begin());
			if (s[u].empty())
				puts("0");
			else {
				printf("%d\n", s[u].begin()->w);
				a[s[u].begin()->id] = 0;
			}
		}
		else 
			Cancle();
	}
	return 0;  // 改数组大小!!!用pair记得改宏定义!!!
}

猜你喜欢

转载自blog.csdn.net/weixin_43851525/article/details/108876949
今日推荐