「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;
}
「IOI2017」西默夫 的一个另类做法的更多相关文章
- 「IOI2017」接线 的另类做法
看到这题,我的第一反应是:这就是一个费用流模型?用模拟费用流的方法? 这应该是可以的,但是我忘记了怎么模拟费用流了IOI不可能考模拟费用流.于是我就想了另外一个方法. 首先我们考虑模拟费用流的模型如下 ...
- Linux 小知识翻译 - 「日志」(log)
这次聊聊「日志」. 「日志」主要指系统或者软件留下的「记录」.出自表示「航海日志」的「logbook」. 经常听说「出现问题的时候,或者程序没有安装自己预期的来运行的时候,请看看日志!」. 确实,记录 ...
- 「SCOI2016」妖怪 解题报告
「SCOI2016」妖怪 玄妙...盲猜一个结论,然后过了,事后一证,然后假了,数据真水 首先要最小化 \[ \max_{i=1}^n (1+k)x_i+(1+\frac{1}{k})y_i \] \ ...
- P4711 「化学」相对分子质量
P4711 「化学」相对分子质量 给你一个字符串让你输出相对分子质量(弱智字符串模拟) 我比赛tm调了两个半小时啊QAQ 希望以后能增加代码力吧,纪念挂代码 Code #include<iost ...
- 「NOI2018」冒泡排序
「NOI2018」冒泡排序 考虑冒泡排序中一个位置上的数向左移动的步数 \(Lstep\) 为左边比它大的数的个数,向右移动的步数 \(Rstep\) 为右边比它大的数的个数,如果 \(Lstep,R ...
- 「SDOI2005」区间
「SDOI2005」区间 传送门 记录每一个位置作为左端点和右端点的出现次数,然后直接考虑差分即可. 参考代码: #include <cstdio> #define rg register ...
- 「POJ3613」Cow Relays
「POJ3613」Cow Relays 传送门 就一个思想:\(N\) 遍 \(\text{Floyd}\) 求出经过 \(N\) 个点的最短路 看一眼数据范围,想到离散化+矩阵快速幂 代码: #in ...
- Python后端日常操作之在Django中「强行」使用MVVM设计模式
扫盲 首先带大家了解一下什么是MVVM模式: 什么是MVVM?MVVM是Model-View-ViewModel的缩写. MVVM是MVC的增强版,实质上和MVC没有本质区别,只是代码的位置变动而已 ...
- 10月1日之后,你新建的GitHub库默认分支不叫「master」了
从 2020 年 10 月 1 日开始,GitHub 上的所有新库都将用中性词「main」命名,取代原来的「master」,因为后者是一个容易让人联想到奴隶制的术语. 这个决定并不是最近才做出的.今年 ...
随机推荐
- oracle的迁移工作
1.创建新数据库用户 1).创建用户和分配权限 sqlplus / as sysdba create user ENFRC_TEST_GZ_TMP identified by ENFRC_TEST_G ...
- Collectors工具类
Collector是专门用来作为Stream的collect方法的参数的:而Collectors是作为生产具体Collector的工具类. Collectors是一个工具类,是JDK预实现Collec ...
- C#高级编程之反射
反射的定义 MSDN定义:反射提供描述程序集.模块和类型的对象(Type类型). 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或访问器字段和属性. 如 ...
- python笔记(1)---数据类型
数据类型 基本的五大数据类型 对python中的变量有如下的五种基本的数据类型: Number数字 list列表 Tuple元组 string字符串 Dictionary字典 1.Number [注意 ...
- FL Studio中的音频剪辑功能
音频剪辑是FL Studio中的特色功能,音频剪辑的目的是保持在播放列表中显示和触发的音频,可以根据需要对它们进行切片和排列.但音频剪辑这个功能在FL Studio的基础版中是没有的. 图1:音频剪辑 ...
- 如何在Mac上安全彻底的卸载软件?
Mac如何卸载软件呢?通常我们的做法都是将应用程序图标移动到废纸篓中,这样就算是将mac软件卸载了,但是这样真的将软件卸载干净了吗?当然没有,一个软件并不是只有应用程序包,他还会包含很多的偏好文件等等 ...
- Vegas技巧分享,如何实现5.1立体声道
Vegas Pro 15除了有各种好用的功能之外,还可以使用此软件制作5.1立体声道的音乐.如果你家里有多个音箱,那么你再也不用担心找不到5.1声道的音乐了,Vegas完全可以帮你实现. 打开简体中文 ...
- SQL server分页的四种方法(算很全面了)
这篇博客讲的是SQL server的分页方法,用的SQL server 2012版本.下面都用pageIndex表示页数,pageSize表示一页包含的记录.并且下面涉及到具体例子的,设定查询第2 ...
- 精尽MyBatis源码分析 - 插件机制
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 红外遥控接收发射原理及ESP8266实现
红外遥控是利用近红外光进行数据传输的一种控制方式.近红外光波长0.76um~1.5um ,红外遥控收发器件波长一般为 0.8um~0.94um ,具有传输效率高,成本低,电路实现简单,抗干扰强等特点, ...