「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」,因为后者是一个容易让人联想到奴隶制的术语. 这个决定并不是最近才做出的.今年 ...
随机推荐
- exec系列函数详解
execve替换进程映像(加载程序):execve系统调用,意味着代码段.数据段.堆栈段和PCB全部被替换.在UNIX中采用一种独特的方法,它将进程创建与加载一个新进程映像分离.这样的好处是有更多的余 ...
- t分布与t检验的一点理解
最近又遇到了t分布及t检验方面的内容,发现有些地方自己当初没有很明白,就又查了些资料,加深了一下自己的理解,这里也将自己的一些理解记录下来. 1. 理论基础--大数定理与中心极限定理 在 ...
- python之路《八》装饰器
装饰器是个好东西啊 那么装饰器是个什么样的东西呢,他又能做些什么呢? 1.为什么装饰器 当我们一个程序已经构建完成,并且已经发布出去了,但是现在需要增加一个活动,例如淘宝给你发送一个今日优惠,或者开启 ...
- 1-03 Java的基本程序设计结构
1-03 Java的基本程序设计结构 3.1 & 3.2 在一个单词中间使用大写字母的方式称为骆驼命名法.以其自身为例,应该写成CamelCase). 与C/C++一样,关键字void表示这个 ...
- readonly和disabled的区别!
Readonly只针对input(text / password)和textarea有效 Disabled对于所有的表单元素都有效 readonly接受值更改可以回传,disable接受改但不回传数据 ...
- Nacos服务发现源码解析
1.Spring服务发现的统一规范 Spring将这套规范定义在Spring Cloud Common中 discovery包下面定义了服务发现的规范 核心接口:DiscoveryClient 用于服 ...
- webug第十五关:什么?图片上传不了?
第十五关:什么?图片上传不了? 直接上传php一句话失败,将content type改为图片 成功
- 关于Boom 3D“夜间模式”的妙用
Boom 3D的"夜间模式"不仅能够优化音效,减小高分贝声音的突然刺激,让你夜晚的聆听效果更加舒适,还能将音效运用到各种环境中. 下面小编就给大家讲一讲如何更好的利用Boom 3D ...
- 追踪聚光特效怎么实现,有Vegas就够了
舞台聚光灯大家一定都不陌生,在电视上某些颁奖活动里,主持人的进场一定伴随着舞台灯光的聚光效果.随着主持人的移动,灯光也随之移动.这里的舞台灯光就起到了一个追踪聚光的效果. Vegas Pro 16 增 ...
- Vegas转场功能的妙用,让片头转场更酷炫
如今视频剪辑已经是一件非常平常的事情了,很多时候我们制作一段或者剪辑一段视频,其实都比较简单,但是如果想要视频显得高级些,这时候就可以给自己的视频制作一个好看的片头了,具体该怎么做呢? 用视频剪辑软件 ...