Luogu: P1653 Monkey (tree / graph storage + reverse thinking edge deletion -- > edge addition)

Posted by pazzy on Wed, 05 Jan 2022 18:53:09 +0100

Rogue: Monkey

A very disgusting question. The meaning of the question cards you first, making you feel that it gives the structure of a tree (one monkey with two hands)

But in reverse, the monkey's hand is a directed edge, and there are double edges or self rings in the graph

A monkey's hand is not holding the other monkey's hand (release both sides to separate), but holding his body. Only when there is no edge between the two monkeys can they separate

Therefore, for data, we should record not only the structure of the tree, but also the structure of the graph (we can not miss information when traversing)

After the drawing is completed, the title will give M operations:

Each time a monkey releases one of its hands, it is equivalent to deleting an edge from our diagram

Also record the time point of the separated landing Monkey (the premise of a monkey landing is to lose contact with monkey 1, because monkey 1 will never land when its tail is hung on the tree)

It is difficult to delete edges in the graph for M times. It takes a lot of time to judge whether there is separation and lose contact with No. 1

So let's think in reverse:

It is difficult to delete edges, but it is relatively easy to add them

We can store the state of the graph after deleting all specific edges, and then add edges one by one in reverse order to observe whether the edge adding operation can make a point or block contact with No. 1

If there is a connection, it is obvious that deleting this edge can also disconnect the connection accordingly

After thinking about it, we can determine that this is the time when the monkey landed (it will not be judged late) (at this time, adding edges can make connections, and adding more edges earlier obviously exists)

The rest is how to judge the connection between edging:

We can define that the marked points are related to point 1

For the graph with all specific edges deleted at the beginning, run dfs again and mark each point (these points obviously won't land in the end)

Then, while enumerating the edges in reverse order, judge whether the two ends of the edge are marked and not marked

For unmarked parts of dfs, only unmarked points are traversed during dfs, so that all points will be traversed only once. The time complexity is O ( n + m ) O(n + m) O(n+m), completely acceptable


#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul (u << 1)
#define ur (u << 1 | 1)
#define fx first
#define fy second
//#pragma GCC optimize(2)
//[blog address]( 
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

const int N = 200010, M = 400010, MM = 3000010;
int INF = 0x3f3f3f3f, mod = 100003;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
int h[N], e[M << 1], ne[M << 1], idx;
int g[N][2], ans[N];
bool vis[N];
struct edge
	int a, x;

void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;

void dfs(int u, int s) {
	ans[u] = s;
	vis[u] = true;//So we need to add a special tag array
	for (int i = h[u]; ~i; i = ne[i]) {
		int j = e[i];
		if (vis[j])continue;//bug - if(ans[j])continue
		//The ans[j] of this question ranges from 0 to M-1, which will lead to dfs dead cycle
		dfs(j, s);

int main() {

	cin >> n >> m;

	for (int i = 1; i <= n; i++) {
		int a, b;
		cin >> a >> b;
		g[i][0] = a, g[i][1] = b;//Storage tree structure

	for (int i = 0; i < m; i++) {
		int u, h;
		cin >> u >> h;

		ed[i] = { u,g[u][h] };//Record the deleted edges
		g[u][h] = -1;//Delete edge

	mem(h, -1);
	for (int i = 1; i <= n; i++) {
		if (g[i][0] != -1)add(i, g[i][0]), add(g[i][0], i);
		if (g[i][1] != -1)add(i, g[i][1]), add(g[i][1], i);
		//Create a graph after deleting a specific edge

	dfs(1, -1);//The initial mark is always the monkey in the tree

	for (int i = m - 1; i >= 0; i--) {
		int u = ed[i].a, v = ed[i].x;
		add(u, v), add(v, u);//Every time you add an edge

		if (!ans[u] && ans[v])dfs(u, i);//Stain and mark the landing time of a group of monkeys
		if (!ans[v] && ans[u])dfs(v, i);

	for (int i = 1; i <= n; i++)
		cout << ans[i] << '\n';

	return 0;
6 4
6 4
5 1
4 -1
-1 3
-1 -1
1 -1

1 1
2 2
1 2
6 1

Topics: C++ Algorithm Graph Theory