zoukankan      html  css  js  c++  java
  • 「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;
    }
    
  • 相关阅读:
    微信机器人-定制消息
    Python实现微信祝福语自动发送
    日常使用 ADB 命令
    python 中的三种等待方式
    Appium自动化测试之环境安装
    Charles 模拟弱网
    Navicat连接MySQL报错-2059
    requests高级用法
    requests基本用法
    Monkey测试环境搭建
  • 原文地址:https://www.cnblogs.com/mathematician/p/12978607.html
Copyright © 2011-2022 走看看