Codeforces Round #673 (Div. 2) F. Graph and Queries (Consolidated search heuristic merge & cancel)

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

Main idea:

Give you an undirected graph. The point weight of each point is different. Ask the operation multiple times. The first query is to output the maximum point weight of the connected block where x is located and change it to 0. The second operation is to delete the xth edge.

solution:

Change the order of creating edges, then all edges deleted in the query can be revoked by consolidating. The order of creating edges is: edges other than the query are given priority, and the order of the edges in the query is reversed (because the stack is first in and out) . Then open a two-dimensional set, save the point weight of each connected block's ancestors, and sort them according to the larger point weight; the operations of merging and canceling are the same as the merge search 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记得改宏定义!!!
}

 

Guess you like

Origin blog.csdn.net/weixin_43851525/article/details/108876949