"Solutions" Luo Gu P5659 tree number

Part 0

General idea

The main idea is to break one by one, and study special data to get common conclusions.

Part 1

Violent part

The violent part is very easy to take, we can directly arrange the whole, and then \ (\ Theta (n) \) to judge the updated answer.

Congratulations on your perfect score

namespace SubtaskForce {
	int cmp[MAXN], ans[MAXN];
	bool vis[MAXN];
	void dfs(int now) { // 全排列
		if (now == n) { // 更新答案
			for (R int i = 1; i <= n; ++i) cmp[id[i]] = i;
			for (R int i = 1; i <= n; ++i) {
				if (cmp[i] < ans[i]) {
					for (R int j = 1; j <= n; ++j) ans[j] = cmp[j];
					break;
				}
				if (cmp[i] > ans[i]) break;
			}
			return ;
		}
		for (R int i = 1; i < n; ++i) {
			if (!vis[i]) {
				vis[i] = 1;
				swap(id[nodes[i].x], id[nodes[i].y]);
				dfs(now + 1);
				swap(id[nodes[i].x], id[nodes[i].y]);
				vis[i] = 0;
			}
		}
	}

	void main() { // 初始化
		for (R int i = 1; i <= n; ++i) vis[i] = 0;
		for (R int i = 1; i <= n; ++i) ans[i] = n - i + 1;
		dfs(1);
		for (R int i = 1; i <= n; ++i) printf("%d ", ans[i]);
		puts("");
	}
}

Part 2

Chrysanthemum diagram part

As far as this question is concerned, the chrysanthemum picture is actually a bit more thoughtful than the data of the chain.

We call the node with degree \ (n-1 \) in the chrysanthemum picture \ (rt \) .

We can find that the deleted edge on the chrysanthemum diagram must be between a certain node and \ (rt \) .

That is to say, no matter what order we delete the edges, it will eventually become a ring.

I made a moving picture demo, if the Luogu blog does not support gif, go directly to this URL Click Here

point.gif

With the conclusion of the ring, there is an obvious greedy way to construct the ring:

According to \ (1,2, \ cdots, n \) sequence number for each selected point on the lower ring own.

In the preparation of the code is also noted that has not connected to the \ (Y_ {n} \) in advance since the death autistic case closed.

namespace SubtaskAss { // 菊花的单词太长了,就取了个差不多的/xyx
	bool vis[MAXN];
	int ans[MAXN];
	struct UninoFindSet {
		int fa[MAXN];

		void init(int limit) {
			for (R int i = 1; i <= limit; ++i)
				fa[i] = i;
		}

		int find(int x) {
			if (x ^ fa[x]) fa[x] = find(fa[x]);
			return fa[x];
		}

		void merge(int x, int y) {
			x = find(x);
			y = find(y);
			if (x ^ y) fa[x] = y;
		}
	} ufs;

	void main() {
		ufs.init(n);
		for (R int i = 1; i <= n; ++i) vis[i] = 0;
		for (R int i = 1; i <= n; ++i) {
			for (R int j = 1; j <= n; ++j) {
				if (!vis[j] && (i == n || ufs.find(j) != ufs.find(id[i]))) {
					vis[j] = 1;
					ans[i] = j;
					ufs.merge(j, id[i]);
					break;
				}
			}
		}
		for (R int i = 1; i <= n; ++i) printf("%d ", ans[i]);
		puts("");
	}
}

part 3

Part of the chain

To tell the truth, part of the chain is actually quite good, but it is still harder to think about than the chrysanthemum picture.

First, it is a fixed operation to shoot the chain into a tree using dfs order.

The chain has a property that each node (except the two ends) has only two degrees.

In other words, each node has two edges except for the end points. And the deletion time of these two sides must be different (nonsense

In other words, there are three cases where the two edges of each node are deleted.

We define \ (order_ {i} \) as the deletion of the left and right sides of the node \ (i \) :

  • 0: 0 means that the left and right sides of this node have not been deleted
  • 1: 1 means that the left side of this node is deleted first
  • 2: 2 means that the right side of this node is deleted first

Now we assume that the left node \ (u \) is going to the right node \ (v \) , then the node between \ (u \) and \ (v \) must be the left first Delete, so \ (order_i = 1, i \ in (u, v) \)

For the two nodes \ (u \) and \ (v \) , the right side must be deleted first, otherwise you do n’t know where to go

So \ (order_ {u} = order_ {v} = 2 \)

The same goes for running from right to left.

The answer is also from small enumeration to large (I enumerate from small to large / xyx)

For example, if we currently enumerate to the node \ (x \) , we want it to go to a point as small as possible

Assuming that the current \ (x \) is in \ (P_ {x} \) , we directly enumerate a \ (P_ {y} \) .

To judge whether a solution is feasible, you only need to judge whether it conflicts with the previous order of edge deletion.

This is done by \ (\ Theta (N ^ 3) \) . We can mark it in dfs, so it is \ (\ Theta (n ^ 2) \) .

namespace SubtaskChain {
	int rnk[MAXN], ans[MAXN], dfn[MAXN];
	int sbc_tot, order[MAXN], vis[MAXN];

	void dfs(int x, int fa) {
		rnk[dfn[x] = ++sbc_tot] = x;
		for (R int i = head[x]; i; i = nxt[i])
			if (to[i] ^ fa) dfs(to[i], x);
	}

	void mark_node(int p1, int p2, int tg) {
		if (p1 != 1 && p1 != n) order[p1] = tg + 1;
		if (p2 != 1 && p2 != n) order[p2] = tg + 1;
		for (R int i = (tg ? p1 + 1 : p2 + 1); i < (tg ? p2 : p1); ++i) order[i] = ((tg ^ 1) + 1);
	}

	int iterate(int x, int tg) {
		int res = n + 1;
		if (order[dfn[x]] == tg + 1) return res;
		for (R int i = dfn[x] + (tg ? -1 : 1); tg ? (i >= 1) : (i <= n); i += (tg ? -1 : 1)) {
			if (order[i] == (tg ^ 1) + 1) {
				if (!vis[i]) res = min(res, rnk[i]);
				break;
			}
			if (!order[i] && !vis[i]) res = min(res, rnk[i]);
		}
		return res;
	}
    int inver_id[MAXN];
	void main() {
		for (R int i = 1; i <= n; ++i) rnk[i] = 0;
		for (R int i = 1; i <= n; ++i) dfn[i] = 0;
		for (R int i = 1; i <= n; ++i) vis[i] = 0;
		for (R int i = 1; i <= n; ++i) order[i] = 0;
		for (R int i = 1; i <= n; ++i) inver_id[id[i]] = i;
		sbc_tot = 0;
		for (R int i = 1; i <= n; ++i) {
			if (in[i] == 1) {
				dfs(i, 0);
				break;
			}
		}
		for (R int i = 1; i <= n; ++i) {
			int left = iterate(inver_id[i], 1);
			int right = iterate(inver_id[i], 0);
			if (left < right) mark_node(dfn[inver_id[i]], dfn[left], 0);
			else left = right, mark_node(dfn[inver_id[i]], dfn[left], 1);
			ans[i] = left;
			vis[dfn[left]] = 1;
		}
		for (R int i = 1; i <= n; ++i) printf("%d ", ans[i]);
		puts("");
	}
}

Part 4

Correct answer

Positive solution

(Can I say that I have read the solution for the remaining 40pts for a long time)

The remaining 40pts is the Click Here I will read this solution

In fact, the data of the meeting chain is basically not far from success.

If you think about it carefully, when we process the chain, we specify the value of the deletion order of the edge with a node.

If we look at the general situation, we can determine a deletion order similar to the topological order, that is, an edge needs to be deleted after an edge is deleted.

For example, the following figure:

889V74.jpg

When we write this deletion order, we can find that this actually constitutes a chain.

Right! Right!

Suppose we now need to delete \ (x \) to \ (y \) node.

Then the judgment rule is as follows:

Illegal situation:

  • There have been a number from \ (x \) been out
  • There is a number that has already reached \ (y \) here
  • There is a number that passes through the outgoing edge of \ (x \) in the same direction
  • There is a number that passes through the outgoing edge of \ (y \) in the same direction
  • Any one of the entry / exit edges is walked once in the same direction by another number
  • Plus the chain formed by the current number \ (x \) has any side that is not on the top
  • Plus the chain formed by the current number \ (y \) has any side that is not on the top
  • After adding the current number, the number after \ (x \) is closed (a ring is formed)
  • After adding the current number, a chain is formed, and any outgoing edge of \ (x \) is not on it.

Legal situation

  • It is legal to exclude all of the above

Direct greed will die terribly.

We can use dfs to find the lowest number as the answer for this round.

namespace SubtaskRandom {
	int mark[MAXN][MAXN], inver_id[MAXN];
	int lave_unwalked[MAXN], fa[MAXN];
	int lave_in[MAXN], lave_out[MAXN];
	int node_from[MAXN], node_to[MAXN];
	int header[MAXN][MAXN], footer[MAXN][MAXN];
	bool vis[MAXN];

	void dfs(int x, int rt) {
		for (R int i = head[x]; i; i = nxt[i]) {
			int y = to[i];
			if (y ^ fa[x]) {
				fa[y] = x;
				vis[y] = 1;
				if (x ^ rt) {
					if (mark[x][y] == x || mark[fa[x]][x] == fa[x]) vis[y] = 0;
					if (mark[x][y] == 0 || mark[fa[x]][x] == 0) vis[y] = 0;
					if (header[x][fa[x]] == node_to[x] && footer[x][y] == node_from[x]
						&& lave_out[x] + lave_in[x] + (lave_unwalked[x] << 1) > 2) vis[y] = 0;
					if (footer[x][y] == fa[x]) vis[y] = 0;
				}
				else {
					if (mark[x][y] == x) vis[y] = 0;
					if (mark[x][y] == 0) vis[y] = 0;
					if (node_from[x]) {
						if (footer[x][y] == node_from[x] && lave_unwalked[x] + lave_in[x] + lave_out[x] != 1)
							vis[y] = 0;
					}
				}
				vis[y] &= vis[x];
				dfs(y, rt);
			}
		}
		if (rt ^ x) {
			if (node_from[x]) vis[x] = 0;
			if (node_to[x]) {
				if (footer[x][node_to[x]] == fa[x] && lave_unwalked[x] + lave_in[x] + lave_out[x] != 1)
					vis[x] = 0;
			}
		}
		else {
			vis[x] = 0;
		}
	}

	void main() {
		for (R int i = 1; i <= n; ++i) node_from[i] = 0;
		for (R int i = 1; i <= n; ++i) node_to[i] = 0;
		for (R int i = 1; i <= n; ++i) lave_in[i] = 0;
		for (R int i = 1; i <= n; ++i) lave_out[i] = 0;
		for (R int i = 1; i <= n; ++i) lave_unwalked[i] = 0;
		for (R int i = 1; i <= n; ++i) inver_id[id[i]] = i;
		for (R int i = 1; i < n; ++i) {
			lave_unwalked[nodes[i].x]++;
			lave_unwalked[nodes[i].y]++;
			mark[nodes[i].x][nodes[i].y] = -1;
			mark[nodes[i].y][nodes[i].x] = -1;
			header[nodes[i].x][nodes[i].y] = nodes[i].y;
			header[nodes[i].y][nodes[i].x] = nodes[i].x;
			footer[nodes[i].x][nodes[i].y] = nodes[i].y;
			footer[nodes[i].y][nodes[i].x] = nodes[i].x;
		}
		for (R int i = 1; i <= n; ++i) {
			for (R int j = 1; j <= n; ++j) fa[j] = 0;
			vis[inver_id[i]] = 1;
			dfs(inver_id[i], inver_id[i]);
			int res = 0;
			for (R int j = 1; j <= n; ++j) {
				if (vis[j]) {
					res = j;
					break;
				}
			}
			printf("%d ", res);
			node_from[res] = fa[res];
			while (fa[res] ^ inver_id[i]) {
				if (~mark[fa[res]][res]) {
					mark[fa[res]][res] = mark[res][fa[res]] = 0;
					lave_in[res]--;
					lave_out[fa[res]]--;
				}
				else {
					mark[fa[res]][res] = mark[res][fa[res]] = fa[res];
					lave_unwalked[res]--;
					lave_out[res]++;
					lave_unwalked[fa[res]]--;
					lave_in[fa[res]]++;
				}
				int t = res;
				res = fa[res];
				header[res][footer[res][t]] = header[res][fa[res]];
				footer[res][header[res][fa[res]]] = footer[res][t];
			}
			if (~mark[fa[res]][res]) {
				mark[fa[res]][res] = 0;
				mark[res][fa[res]] = 0;
				lave_in[res]--;
				lave_out[inver_id[i]]--;
			}
			else {
				mark[fa[res]][res] = fa[res];
				mark[res][fa[res]] = fa[res];
				lave_unwalked[res]--;
				lave_out[res]++;
				lave_unwalked[inver_id[i]]--;
				lave_in[inver_id[i]]++;
			}
			node_to[inver_id[i]] = res;
		}
		puts("");
	}
}

Complete code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>

using namespace std;

char buf[1 << 21], *p1 = buf, *p2 = buf;
#ifndef ONLINE_JUDGE
#define gc() getchar()
#else
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
#endif
#define is() (ch >= '0' && ch <= '9')
#define R register

template < class Type >
void read(Type& a) {
	a = 0; bool f = 0; char ch;
	while (!(ch = gc(), is())) if (ch == '-') f = 1;
	while (is()) a = (a << 3) + (a << 1) + (ch ^ '0'), ch = gc();
	a = (f ? -a : a);
}

template < class Type, class... Args >
void read(Type& t, Args&... args) {
	read(t), read(args...);
}

const int MAXN = 2000 + 5;
int T, n, max_in, id[MAXN];
int head[MAXN], nxt[MAXN << 1];
int tot, in[MAXN], to[MAXN << 1];
struct EdgeNode {
	int x, y;
} nodes[MAXN];

EdgeNode make_edge(int x, int y) {
	EdgeNode res;
	res.x = x;
	res.y = y;
	return res;
}

void add(int x, int y) {
	to[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}

namespace SubtaskForce {
	int cmp[MAXN], ans[MAXN];
	bool vis[MAXN];
	void dfs(int now) {
		if (now == n) {
			for (R int i = 1; i <= n; ++i) cmp[id[i]] = i;
			for (R int i = 1; i <= n; ++i) {
				if (cmp[i] < ans[i]) {
					for (R int j = 1; j <= n; ++j) ans[j] = cmp[j];
					break;
				}
				if (cmp[i] > ans[i]) break;
			}
			return ;
		}
		for (R int i = 1; i < n; ++i) {
			if (!vis[i]) {
				vis[i] = 1;
				swap(id[nodes[i].x], id[nodes[i].y]);
				dfs(now + 1);
				swap(id[nodes[i].x], id[nodes[i].y]);
				vis[i] = 0;
			}
		}
	}

	void main() {
		for (R int i = 1; i <= n; ++i) vis[i] = 0;
		for (R int i = 1; i <= n; ++i) ans[i] = n - i + 1;
		dfs(1);
		for (R int i = 1; i <= n; ++i) printf("%d ", ans[i]);
		puts("");
	}
}

namespace SubtaskAss {
	bool vis[MAXN];
	int ans[MAXN];
	struct UninoFindSet {
		int fa[MAXN];

		void init(int limit) {
			for (R int i = 1; i <= limit; ++i)
				fa[i] = i;
		}

		int find(int x) {
			if (x ^ fa[x]) fa[x] = find(fa[x]);
			return fa[x];
		}

		void merge(int x, int y) {
			x = find(x);
			y = find(y);
			if (x ^ y) fa[x] = y;
		}
	} ufs;

	void main() {
		ufs.init(n);
		for (R int i = 1; i <= n; ++i) vis[i] = 0;
		for (R int i = 1; i <= n; ++i) {
			for (R int j = 1; j <= n; ++j) {
				if (!vis[j] && (i == n || ufs.find(j) != ufs.find(id[i]))) {
					vis[j] = 1;
					ans[i] = j;
					ufs.merge(j, id[i]);
					break;
				}
			}
		}
		for (R int i = 1; i <= n; ++i) printf("%d ", ans[i]);
		puts("");
	}
}

namespace SubtaskChain {
	int rnk[MAXN], ans[MAXN], dfn[MAXN];
	int sbc_tot, order[MAXN], vis[MAXN];

	void dfs(int x, int fa) {
		rnk[dfn[x] = ++sbc_tot] = x;
		for (R int i = head[x]; i; i = nxt[i])
			if (to[i] ^ fa) dfs(to[i], x);
	}

	void mark_node(int p1, int p2, int tg) {
		if (p1 != 1 && p1 != n) order[p1] = tg + 1;
		if (p2 != 1 && p2 != n) order[p2] = tg + 1;
		for (R int i = (tg ? p1 + 1 : p2 + 1); i < (tg ? p2 : p1); ++i) order[i] = ((tg ^ 1) + 1);
	}

	int iterate(int x, int tg) {
		int res = n + 1;
		if (order[dfn[x]] == tg + 1) return res;
		for (R int i = dfn[x] + (tg ? -1 : 1); tg ? (i >= 1) : (i <= n); i += (tg ? -1 : 1)) {
			if (order[i] == (tg ^ 1) + 1) {
				if (!vis[i]) res = min(res, rnk[i]);
				break;
			}
			if (!order[i] && !vis[i]) res = min(res, rnk[i]);
		}
		return res;
	}
    int inver_id[MAXN];
	void main() {
		for (R int i = 1; i <= n; ++i) rnk[i] = 0;
		for (R int i = 1; i <= n; ++i) dfn[i] = 0;
		for (R int i = 1; i <= n; ++i) vis[i] = 0;
		for (R int i = 1; i <= n; ++i) order[i] = 0;
		for (R int i = 1; i <= n; ++i) inver_id[id[i]] = i;
		sbc_tot = 0;
		for (R int i = 1; i <= n; ++i) {
			if (in[i] == 1) {
				dfs(i, 0);
				break;
			}
		}
		for (R int i = 1; i <= n; ++i) {
			int left = iterate(inver_id[i], 1);
			int right = iterate(inver_id[i], 0);
			if (left < right) mark_node(dfn[inver_id[i]], dfn[left], 0);
			else left = right, mark_node(dfn[inver_id[i]], dfn[left], 1);
			ans[i] = left;
			vis[dfn[left]] = 1;
		}
		for (R int i = 1; i <= n; ++i) printf("%d ", ans[i]);
		puts("");
	}
}

namespace SubtaskRandom {
	int mark[MAXN][MAXN], inver_id[MAXN];
	int lave_unwalked[MAXN], fa[MAXN];
	int lave_in[MAXN], lave_out[MAXN];
	int node_from[MAXN], node_to[MAXN];
	int header[MAXN][MAXN], footer[MAXN][MAXN];
	bool vis[MAXN];

	void dfs(int x, int rt) {
		for (R int i = head[x]; i; i = nxt[i]) {
			int y = to[i];
			if (y ^ fa[x]) {
				fa[y] = x;
				vis[y] = 1;
				if (x ^ rt) {
					if (mark[x][y] == x || mark[fa[x]][x] == fa[x]) vis[y] = 0;
					if (mark[x][y] == 0 || mark[fa[x]][x] == 0) vis[y] = 0;
					if (header[x][fa[x]] == node_to[x] && footer[x][y] == node_from[x]
						&& lave_out[x] + lave_in[x] + (lave_unwalked[x] << 1) > 2) vis[y] = 0;
					if (footer[x][y] == fa[x]) vis[y] = 0;
				}
				else {
					if (mark[x][y] == x) vis[y] = 0;
					if (mark[x][y] == 0) vis[y] = 0;
					if (node_from[x]) {
						if (footer[x][y] == node_from[x] && lave_unwalked[x] + lave_in[x] + lave_out[x] != 1)
							vis[y] = 0;
					}
				}
				vis[y] &= vis[x];
				dfs(y, rt);
			}
		}
		if (rt ^ x) {
			if (node_from[x]) vis[x] = 0;
			if (node_to[x]) {
				if (footer[x][node_to[x]] == fa[x] && lave_unwalked[x] + lave_in[x] + lave_out[x] != 1)
					vis[x] = 0;
			}
		}
		else {
			vis[x] = 0;
		}
	}

	void main() {
		for (R int i = 1; i <= n; ++i) node_from[i] = 0;
		for (R int i = 1; i <= n; ++i) node_to[i] = 0;
		for (R int i = 1; i <= n; ++i) lave_in[i] = 0;
		for (R int i = 1; i <= n; ++i) lave_out[i] = 0;
		for (R int i = 1; i <= n; ++i) lave_unwalked[i] = 0;
		for (R int i = 1; i <= n; ++i) inver_id[id[i]] = i;
		for (R int i = 1; i < n; ++i) {
			lave_unwalked[nodes[i].x]++;
			lave_unwalked[nodes[i].y]++;
			mark[nodes[i].x][nodes[i].y] = -1;
			mark[nodes[i].y][nodes[i].x] = -1;
			header[nodes[i].x][nodes[i].y] = nodes[i].y;
			header[nodes[i].y][nodes[i].x] = nodes[i].x;
			footer[nodes[i].x][nodes[i].y] = nodes[i].y;
			footer[nodes[i].y][nodes[i].x] = nodes[i].x;
		}
		for (R int i = 1; i <= n; ++i) {
			for (R int j = 1; j <= n; ++j) fa[j] = 0;
			vis[inver_id[i]] = 1;
			dfs(inver_id[i], inver_id[i]);
			int res = 0;
			for (R int j = 1; j <= n; ++j) {
				if (vis[j]) {
					res = j;
					break;
				}
			}
			printf("%d ", res);
			node_from[res] = fa[res];
			while (fa[res] ^ inver_id[i]) {
				if (~mark[fa[res]][res]) {
					mark[fa[res]][res] = mark[res][fa[res]] = 0;
					lave_in[res]--;
					lave_out[fa[res]]--;
				}
				else {
					mark[fa[res]][res] = mark[res][fa[res]] = fa[res];
					lave_unwalked[res]--;
					lave_out[res]++;
					lave_unwalked[fa[res]]--;
					lave_in[fa[res]]++;
				}
				int t = res;
				res = fa[res];
				header[res][footer[res][t]] = header[res][fa[res]];
				footer[res][header[res][fa[res]]] = footer[res][t];
			}
			if (~mark[fa[res]][res]) {
				mark[fa[res]][res] = 0;
				mark[res][fa[res]] = 0;
				lave_in[res]--;
				lave_out[inver_id[i]]--;
			}
			else {
				mark[fa[res]][res] = fa[res];
				mark[res][fa[res]] = fa[res];
				lave_unwalked[res]--;
				lave_out[res]++;
				lave_unwalked[inver_id[i]]--;
				lave_in[inver_id[i]]++;
			}
			node_to[inver_id[i]] = res;
		}
		puts("");
	}
}

signed main() {
	for (read(T); T; --T) {
		read(n);
		for (R int i = 1, x; i <= n; ++i) read(x), id[x] = i;
		for (R int i = 1; i <= n; ++i) head[i] = in[i] = 0;
		tot = 0, max_in = 0;
		for (R int i = 1; i < n; ++i) {
			int x, y;
			read(x, y);
			add(x, y);
			add(y, x);
			++in[x], ++in[y];
			nodes[i] = make_edge(x, y);
			max_in = max(max_in, max(in[x], in[y]));
		}
		if (n <= 10) SubtaskForce::main();
		else if (max_in == n - 1) SubtaskAss::main();
		else if (max_in == 2) SubtaskChain::main();
		else SubtaskRandom::main();
	}
}

Guess you like

Origin www.cnblogs.com/orchid-any/p/12731058.html