2020 Niuke Summer Multi-School Training Camp (Eighth Game) A.All-Star Game (Time Divide and Conquer Line Segment Tree)

Time limit: C/C++ 5 seconds, other languages ​​10 seconds
Space limit: C/C++ 262144K, other languages ​​524288K
64bit IO Format: %lld

Title description

In Apollo's country, basketball is the most popular sport. There are n basketball players and m basketball fans. Bastketball players are numbered from 1 to n. Basketball fans are numbered from 1 to m.
A basketball fan can be a fan of multiple players.
Basketball fan i is like to watch the game of player j if one of the following conditions is met:

  • Basketball fan i is a fan of basketball player j.
  • There is a fan i' and a player j', both fan i and fan i' are like to watch the game of player j', and fan i' is like to watch the game of player j.

The All-Star Game is an annual exhibition basketball game. You need to select some players into the All-Star Game. A fan will watch the All-Star Game if he is like to watch the game of one selected player (i.e. at least one of the players that the fan is like to watch was selected). You need to decide the minimum number of players need to be selected into All-Star Game so that all basketball fans will watch the game.
Initially, some basketball players will have some fans. There are q changes to the relationship between basketball players and basketball fans.
After each of q changes, print one integer --- the minimum number of players which you have to select to make all basketball fans will watch the game. Print "-1" if it is impossible.

Enter description:

The first line contains three integers n, m and q (1≤n,m,q≤2×1051 \le n, m, q \le 2 \times 10^51≤n,m,q≤2×105) - the number of basketball players, the number of basketball fans and the number of changes respectively.

Then n lines follow. The i-th line starts with one integer kik_iki​ (0≤ki≤m0 \le k_i \le m0≤ki​≤m) - the initial number of fans of basketball player i, then contains kik_iki​ different integers - present the fans of player i.

∑i=1nki≤5×105\sum_{i=1}^n k_i \le 5 \times 10^5∑i=1n​ki​≤5×105.

Then q lines follow. The i-th line contains two integers x and y (1≤x≤m1 \le x \le m1≤x≤m, 1≤y≤n1 \le y \le n1≤y≤n). If fan x is a fan of basketball player y, then fan x will be not a fan of player y, otherwise, fan x will be a fan of player y.

Output description:

After each change print one integer --- the minimum number of players need to be selected into All-Star Game so that all basketball fans will watch the game. Pritn "-1" if it is impossible.

Example 1

enter

copy

4 4 6

2 1 2

0

2 2 3

1 4

4 2

2 3

2 1

2 2

4 2

4 1

4 4 6
2 1 2
0
2 2 3
1 4
4 2
2 3
2 1
2 2
4 2
4 1

Output

2 3 -1 3 4 3

2
3
-1
3
4
3

If the meaning of the question is not easy to explain, just assume that you already know it ^_^

solution:

First of all, this is a problem of dynamically maintaining connected blocks , and the problem is not forced to be online, then you can use the time divide and conquer line tree (hereinafter referred to as SJFZ) to solve it.

SJFZ simply understands that it takes the number of the query as a subscript , and then marks the time period when the edge uv appears , merges it when it appears , and cancels the subtree after the recursion is completed.

An analysis shows that the result of each time is the number of connected blocks containing fans . If a connected block only has fans, then the output is -1, because no player can make him go to the game. So we only need to maintain the number of connected blocks containing fans while writing SJFZ , and whether there are fans alone into a connected block to determine whether the answer is -1.

Number of connected blocks of fans : Initially, each fan can be marked. hav[x] = true means that the connected block contains fans. If x is merged into y, then hav[y] |= hav[x], when revoking Just update and go back.

Are there fans individually connected into connected blocks : Just mark them when fans connect the first edge and delete the last edge.

If you have already learned SJFZ, just look at the merge and undo functions. The others are templates.

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 = 2e5 + 100;
const int M = 7e5 + 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 u, v, id;
};
struct Node
{
	int x, y, l, r;
}st[M];
vector <pir> ver[M * 4];
vector <node> ask[N];
int fz[N * 2], sz[N * 2], n, m, q;
map <pir, int> vis;
map <int, int> fan;
int ans[N], fans, cut, top;
bool ok[N * 2];   // 是否有fans

void Init() {
	for (int i = 1; i <= n + m; i++) {
		fz[i] = i, sz[i] = 1;
		if (i > n)
			ok[i] = true;
	}
	cut = fans = m;  // 连通块,单独粉丝
}
int Find(int x) {
	while (x != fz[x])
		x = fz[x];
	return x;
}
void Merge(int x, int y) {
	x = Find(x), y = Find(y);
	if (x == y) {
		st[++top] = { 0, 0 };
		return;
	}
	if (sz[x] > sz[y])
		swap(x, y);
	fz[x] = y, sz[y] += sz[x];
	st[++top] = { x, y, ok[x], ok[y] }; // 保留当前状态
	if (ok[x] && ok[y]) // fans合并
		cut--;
	ok[y] |= ok[x];

	if (x > n && sz[x] == 1) // 单独的fans少了
		fans--;
}
void Cancle() {
	int x = st[top].x, y = st[top].y;
	int ok1 = st[top].l, ok2 = st[top].r;
	top--;
	if (!x || !y)
		return;
	fz[x] = x, sz[y] -= sz[x];
	ok[x] = ok1, ok[y] = ok2;
	if (ok1 && ok2)  // 都有fans断开
		cut++;
	if (x > n && sz[x] == 1)
		fans++;
}
#define ls (o << 1)
#define rs (ls | 1)
void Update(int o, int L, int R, int l, int r, pir val) {
	if (L >= l && R <= r)
		ver[o].push_back(val);
	else {
		int mid = (L + R) >> 1;
		if (mid >= l)
			Update(ls, L, mid, l, r, val);
		if (mid < r)
			Update(rs, mid + 1, R, l, r, val);
	}
}
void Ask(int o, int L, int R) {
	for (auto it : ver[o])
		Merge(it.first, it.second);
	if (L == R) {
		for (auto it : ask[L]) {
			int u = it.u, v = it.v;
			ans[it.id] = fans ? -1 : cut;
		}
	}
	else {
		int mid = (L + R) >> 1;
		Ask(ls, L, mid), Ask(rs, mid + 1, R);
	}
	int sz = SZ(ver[o]);
	while (sz--)
		Cancle();
}

int main()
{
#ifdef OlaMins
	freopen("D:/input.txt", "r", stdin);
	//freopen("D:/output.txt", "w", stdout);
#endif
	
	cin >> n >> m >> q;
	Init();
	for (int i = 1; i <= n; i++) {
		int k, u;
		sc("%d", &k);
		while (k--) {
			sc("%d", &u);  // fans
			u += n;
			vis[{u, i}] = 1;
		}
	}
	for (int i = 2; i <= q + 1; i++) {
		int x, y;
		sc("%d %d", &x, &y); // fans, player
		x += n; 

		if (vis.count({ x, y })) {  // 有边,此时要删除
			int lst = vis[{x, y}];
			vis.erase({ x, y });
			Update(1, 1, q + 1, lst, i - 1, { x, y });
		}
		else  // 无边,标记时间
			vis[{x, y}] = i;
		ask[i].push_back({ x, y, i });
	}
	for (auto it : vis)
		Update(1, 1, q + 1, it.second, q + 1, { it.first.first, it.first.second });
	Ask(1, 1, q + 1);
	for (int i = 2; i <= q + 1; i++)
		printf("%d\n", ans[i]);
	return 0; // 改数组大小!!!用pair改宏定义!!!
}

 

Guess you like

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