「IOI2017」西默夫 的一个另类做法

我们发现如果我们有一个环套树的话,那么我们可以把这个环套树去掉每一条环上的边\(e\),问一遍有多少御道在这棵树上。假设删去\(e\)后答案为\(A_e\)
如果答案全部一样,那么说明环上的边都不在御道里面(不可能都在)。否则设答案有\(k\)\(k + 1\)两种。那么如果\(A_e = k\),那么\(e\)在,否则\(e\)就不在了。

我们先随便建一棵生成树,然后先试图确定每条树边是否在御道里面。我们依次考虑每一条非树边,如果加入这条非树边\(e\)后,边双连通分量个数变小了,那么我们考虑它让哪些桥消失。这些桥均在一个环上。那么我们把这个环中所有的桥边、\(e\)和恰好一条非桥边(如果存在的话)给询问一遍,就可以类似地得到\(e\)和所有桥边的状态了!最后所有桥边一定要被选,否则连通性会出问题。

注意到每一步的复杂度可以被边双连通分量的减少量bound住,所以这一步询问次数为\(O(n)\)

之后我们把还未确定状态的那些边定向,方向为编号较大的点到编号较小的点。然后对每个点的出边,我们用分治的方法来求出每条边是否是御道。

这里我们考虑给定一个出边的子集,找到一棵生成树使得它恰好包含这些出边的子集,而不包含其它出边,且它包含的其它的边状态已知。那么我们询问这棵树,就可以得到有多少出边是御道了。分治大概就是说每次询问左边一半的个数,然后如果有的话就递归左边,类似地判断是否要递归右边。

由于御道个数为\(n - 1\),而对每个点分治的复杂度为这个点的出边的御道个数*\(\log n\),所以总询问次数为\(O(n \log n)\),可以通过此题!

代码如下:

#include "simurgh.h"
#include <bits/stdc++.h>
using namespace std;

const int N = 505, M = N * N;

int V, E, rt[N], col[N], s[M], t[M], ans[M];
vector<int> tree[N], vec;
bool intree[M];
int findroot (int u) {
	return rt[u] == u ? u : rt[u] = findroot(rt[u]);
}
int find_tree () {
	for (int i = 0; i < E; i++) {
		int x = findroot(s[i]), y = findroot(t[i]);
		if (x != y) {
			intree[i] = true, rt[x] = y;
			vec.push_back(i);
			tree[s[i]].push_back(i);
			tree[t[i]].push_back(i);
		}
	}
	return count_common_roads(vec);
}

int par[N], path[N];
void dfs (int u, int fa) {
	for (int i = 0; i < tree[u].size(); i++) {
		int id = tree[u][i], v = s[id] == u ? t[id] : s[id];
		if (v != fa) {
			par[v] = u, path[v] = id;
			dfs(v, u);
		}
	}
}
vector<int> get_route (int u, int v) {
	vector<int> route;
	dfs(u, -1);
	for (int i = v; i != u; i = par[i]) {
		if (ans[path[i]] < 0) route.push_back(path[i]);
	}
	return route;
}
int extra (int u, int v) {
	for (int i = v; i != u; i = par[i]) {
		if (ans[path[i]] >= 0) return path[i];
	}
	return -1;
}

void replace (int e1, int e2) {
	for (int i = 0; i < vec.size(); i++) {
		if (vec[i] == e1) vec[i] = e2;
	}
}
void shrink (int u, int v) {
	for (int i = v; i != u; i = par[i]) {
		if (ans[path[i]] < 0) {
			int c = col[i];
			for (int j = 0; j < V; j++) {
				if (col[j] == c) col[j] = col[u];
			}
		}
	}
}
void query_tree () {
	int total = find_tree();
	for (int i = 0; i < E; i++) {
		if (!intree[i] && col[s[i]] != col[t[i]]) {
			vector<int> route = get_route(s[i], t[i]), res;
			shrink(s[i], t[i]);
			bool flag[3] = {false};
			for (int j = 0; j < route.size(); j++) {
				replace(route[j], i);
				res.push_back(total - count_common_roads(vec));
				flag[res[j] + 1] = true, replace(i, route[j]);
			}

			if (flag[0] || flag[2]) ans[i] = flag[0] ? 1 : 0;
			else {
				int id = extra(s[i], t[i]);
				if (id) {
					replace(id, i);
					ans[i] = count_common_roads(vec) - total + ans[id];
					replace(i, id);
				}
				else ans[i] = 0;
			}
			for (int j = 0; j < route.size(); j++) ans[route[j]] = ans[i] + res[j];
		}
	}
	for (int i = 0; i < E; i++) {
		if (intree[i] && ans[i] < 0) ans[i] = 1;
	}
}

vector<int> g[N];
int query (int u, int l, int r) {
	int num = 0;
	vec.clear();
	for (int i = 0; i < V; i++) rt[i] = i;
	for (int i = l; i <= r; i++) {
		int id = g[u][i], v = s[id] == u ? t[id] : s[id];
		rt[findroot(u)] = rt[findroot(v)];
		vec.push_back(id);
	}
	for (int i = 0; i < E; i++) {
		if (intree[i]) {
			int x = findroot(s[i]), y = findroot(t[i]);
			if (x != y) {
				vec.push_back(i);
				rt[x] = y, num += ans[i];
			}
		}
	}
	return count_common_roads(vec) - num;
}
void solve (int u, int l, int r, int num) {
	int mid = l + r >> 1;
	if (l == r) ans[g[u][mid]] = (bool)num;
	else {
		int num_ = num ? query(u, l, mid) : 0;
		solve(u, l, mid, num_), solve(u, mid + 1, r, num - num_);
	}
}

vector<int> find_roads (int n, vector<int> u, vector<int> v) {
	V = n, E = u.size();
	for (int i = 0; i < V; i++) col[i] = rt[i] = i;
	for (int i = 0; i < E; i++) {
		ans[i] = -1, intree[i] = false;
		s[i] = u[i], t[i] = v[i];
	}

	query_tree();
	for (int i = 0; i < E; i++) {
		if (ans[i] < 0) g[max(s[i], t[i])].push_back(i);
	}

	for (int i = 0; i < V; i++) {
		if (!g[i].empty()) {
			int num = query(i, 0, g[i].size() - 1);
			solve(i, 0, g[i].size() - 1, num);
		}
	}

	vector<int> tree;
	for (int i = 0; i < E; i++) {
		if (ans[i]) tree.push_back(i);
	}
	return tree;
}
posted @ 2020-05-28 10:00  unzcjouhi  阅读(532)  评论(0编辑  收藏  举报