[CF1515F] Phoenix and Earthquake(图论推导,构造)
题面
在紧张又忙碌地准备联合省选时,发生了大地震,把原本要参赛的
n
n
n 个城市之间的全部
m
m
m 条道路震垮了,使得原本互相都能到达的这
n
n
n 个城市无法交通了。现在,需要紧急恢复
n
−
1
n-1
n−1 条原来的道路,使得任意两个城市可以互相到达。
好在每个城市分别存有
a
i
a_i
ai 吨沥青。修复每条道路需要
x
x
x 吨沥青,如果两个城市
i
i
i 和
j
j
j 之间有一条损坏的道路,且两个城市的沥青总量不小于
x
x
x 吨,那么就可以消耗这两个城市的
x
x
x 吨沥青来修路。修好了路之后,装载沥青的卡车就可以在路上跑,在这一部分连通的城市中任意地来往运送沥青了。
给你道路的信息,以及一开始每个城市分别有的沥青吨数
a
i
a_i
ai ,问能否让
n
n
n 个城市连通,如果能,就输出任意一种修路的方案(按时间顺序输出恢复的道路序号)
题解
首先判断无解:如果
∑
a
i
<
(
n
−
1
)
x
\sum a_i <(n-1)x
∑ai<(n−1)x ,那么明显不可能连通了,因为总沥青不足。
接着,我们要证明其余的情况一定有解。
首先,如果某个时刻
∑
a
i
′
≥
(
n
′
−
1
)
∗
x
\sum a'_i\geq (n'-1)*x
∑ai′≥(n′−1)∗x ,即此时每个连通块缩成一个点,也符合上面的条件,那么一定有某两个城市之间是可以修路的。
- 证明:用反证法,假设所有路都不能修,即
∀
(
i
,
j
)
∈
E
′
,
a
i
′
+
a
j
′
<
x
\forall (i,j)\in E',a'_i+a'_j<x
∀(i,j)∈E′,ai′+aj′<x。
那么我们取∀
S
⊆
E
′
\forall S\sube E'
∀S⊆E′ 满足
∣
S
∣
=
n
′
−
1
|S|=n'-1
∣S∣=n′−1,都有
∀
(
i
,
j
)
∈
S
′
,
a
i
′
+
a
j
′
<
x
\forall (i,j)\in S',a'_i+a'_j<x
∀(i,j)∈S′,ai′+aj′<x (这很废话),
然后由于不等号同向,可以把这n
−
1
n-1
n−1 个不等式左右两边都加起来,
得到:∑
(
i
,
j
)
∈
S
′
a
i
′
+
a
j
′
<
(
n
′
−
1
)
x
\sum_{(i,j)\in S'}a'_i+a'_j<(n'-1)x
∑(i,j)∈S′ai′+aj′<(n′−1)x ,
令第i
i
i 个点的度数为
d
i
′
d'_i
di′ ,则
∑
a
i
′
⋅
d
i
′
<
(
n
′
−
1
)
x
\sum a'_i\cdot d'_i<(n'-1)x
∑ai′⋅di′<(n′−1)x,
由于图连通,所以∀
d
i
≥
1
\forall d_i\geq1
∀di≥1,再加上
a
i
a_i
ai 肯定非负,因此
∑
a
i
′
≤
∑
a
i
′
⋅
d
i
′
<
(
n
′
−
1
)
x
\sum a'_i\leq\sum a'_i\cdot d'_i<(n'-1)x
∑ai′≤∑ai′⋅di′<(n′−1)x这和
∑
a
i
′
≥
(
n
′
−
1
)
∗
x
\sum a'_i\geq (n'-1)*x
∑ai′≥(n′−1)∗x 是矛盾的,假设不成立。因此一定有某两个城市之间可以修路,证毕。
然后,修完一个路过后,相当于
∑
a
i
′
\sum a'_i
∑ai′ 变成了
∑
a
i
′
−
x
\sum a'_i-x
∑ai′−x,
(
n
′
−
1
)
∗
x
(n'-1)*x
(n′−1)∗x 变成了
(
n
′
−
1
)
∗
x
−
x
(n'-1)*x-x
(n′−1)∗x−x,不等式两边同减
x
x
x ,仍然满足上面的关系。所以可以一直修到最后把树修完。
有解了,也就是说 YES/NO
的问题解决了,我们可以想想怎么构造了。
利用上面证明的性质,我们发现对于
∀
S
⊆
E
,
∣
S
∣
=
n
−
1
\forall S\sube E,|S|=n-1
∀S⊆E,∣S∣=n−1,树
T
=
(
V
,
S
)
T=(V,S)
T=(V,S) 都可以是最终的结果,那么我们就先随便取一棵生成树。
然后,构造方法就千奇百怪了,大都随便过,在性能上却有差异,这里笔者给一个比较好的方法。
有这么一种好写好调不用太多判断的构造方法:
用并查集维护缩点。先随便定个根,开始 dfs
,回溯的时候如果可以,就把自己和父节点合并(修路),回溯到根节点时,下面剩余的后代彼此之间肯定不能修边了(不然回溯之前一定会合并),由上面的性质可以得出,此时只有根节点可以往下合并,那么就从根节点开始像吸面条一样把后代全部合并过来就行了,可以按照深度从小到大和根合并,也可以用其他方法都行,笔者是回溯的时候用栈存了没有修的边,最后从栈顶依次拿出来。
CODE
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 300005
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
#define eps 1e-9
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
int n,m,i,j,s,o,k;
int a[MAXN],fe[MAXN];
int b[MAXN],rk[MAXN],X;
bool cmp(int x,int y) {return a[x] > a[y];}
bool f[MAXN];
struct it{
int v,id;it(){v=id=0;}
it(int V,int I){v=V;id=I;}
};
vector<it> g[MAXN];
int d[MAXN];
priority_queue<int,vector<int>,greater<int> > q;
int as[MAXN],cn;
int fa[MAXN];
LL sum[MAXN];
int findf(int x) {return x==fa[x] ? x:(fa[x]=findf(fa[x]));}
void UNION(int a,int b) {fa[findf(a)]=findf(b);}
bool unionSet(int a,int b) {
int u = findf(a),v = findf(b);
if(d[u] < d[v]) swap(u,v);
if(sum[u]+sum[v] < X) return 0;
fa[u] = v; sum[v] += sum[u] - X;
return 1;
}
stack<int> ad;
void dfs(int x,int ff,int fe) {
d[x] = d[ff] + 1;
for(int i = 0;i < (int)g[x].size();i ++) {
int y = g[x][i].v,id = g[x][i].id;
if(y != ff) {
dfs(y,x,id);
}
}
if(ff == x) return ;
else if(unionSet(x,ff)) as[++ cn] = fe;
else ad.push(fe);
return ;
}
int main() {
n = read();m = read();X = read();
LL sma = 0;
for(int i = 1;i <= n;i ++) {
a[i] = read();b[i] = i; fa[i] = i;
sma += a[i]; sum[i] = a[i];
}
sort(b + 1,b + 1 + n,cmp);
for(int i = 1;i <= n;i ++) rk[b[i]] = i;
if(sma < (n-1) *1ll* X) {
printf("NO\n");
return 0;
}
for(int i = 1;i <= m;i ++) {
s = read();o = read();
if(findf(s) != findf(o)) {
UNION(s,o);
g[s].push_back(it(o,i));
g[o].push_back(it(s,i));
}
}
for(int i = 1;i <= n;i ++) fa[i] = i,sum[i] = a[i];
dfs(1,1,0);
printf("YES\n");
while(!ad.empty()) as[++ cn] = ad.top(),ad.pop();
for(int i = 1;i < n;i ++) {
printf("%d\n",as[i]);
}
return 0;
}
[CF1515F] Phoenix and Earthquake(图论推导,构造)的更多相关文章
- Luogu3524 POI2011 Party 图论、构造
题目传送门:https://www.luogu.org/problemnew/show/P3524 大意:给一个$N$个点的图,其中一定有一个大小为$\frac{2}{3}N$的团,程序需给出一个大小 ...
- Gym - 101503I 利用到图论的构造
比赛的时候没有注意到 给出的up矩阵 能使我们随便选一列 确定这一列的rank 这样我们得出每一行列的rank 进行构图 大->小 然后从大到小放 当前放的点 和他有因果关系并且比他大的点必须已 ...
- [SDOI2019]热闹又尴尬的聚会(图论+set+构造)
据说原数据可以让复杂度不满的暴力O(Tn^2)过掉……O(Tn^2)方法类似于codeforces一场div2的E题 有一种比较好的方法:每次找出原图G中度最小的点加入q,然后将相邻的点加入新图G'. ...
- python全栈开发 * 14 知识点汇总 * 180530
14 生成器表达式 内置函数# 一.迭代器 (补充)# 1.如何判断迭代对象,迭代器# (1).dir(obj)检测对象有没有iter方法,然后it=obj.__iter__() 获取迭代器 , it ...
- 函数式JS: 原来promise是这样的monad
转载请注明出处: http://hai.li/2017/03/27/prom... 背景 上篇文章 函数式JS: 一种continuation monad推导 得到了一个类似promise的链式调用, ...
- CF97C Winning Strategy 构造、图论
题目传送门:http://codeforces.com/problemset/problem/97/C 题意:给出$n$与一个范围在$[0,1]$内的递增序列$P_0-P_n$,试构造一个无穷序列$\ ...
- CCO2017 Vera and Trail Building 构造+图论
正解:构造+图论 解题报告: 找了半天才找到的传送门! 先简要表达下题意 一个图上,如果存在(a,b)满足a<b且存在从a到b再回到a的路径,每条道路被经过至多一次,我们称(a,b)为完美点对试 ...
- AES128加密-S盒和逆S盒构造推导及代码实现
文档引用了<密码编码学与网络安全--原理和实践>里边的推导过程,如有不妥,请与我联系修改. 文档<FIPS 197>高级加密标准AES,里边有个S盒构造,涉及到了数论和有限域的 ...
- HIT手 | 机械电气构造简述和微分运动学及静力学的简单推导
机械结构电气构造简述 HIT手有四个手指,每个手指4个关节,其中第一和第二个关节正交,第三和第四个关节机械耦合,故只有3个自由度,另外大拇指多了一个相对手掌运动的自由度,故一只手掌总共有13各个自 ...
随机推荐
- 如何优雅的使用MyBatis?
本文目录 什么是 MyBatis ? 映射器(mappers) typeAliases 类型别名减少类完全限制名的冗余 处理枚举类型 多行插入 重用 SQL 代码段,消除重复 字符串替换#{}和${ ...
- 20天等待,申请终于通过,安装和体验IntelliJ IDEA新UI预览版
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于IDEA的预览版 IDEA会启用新的UI,这事情之 ...
- 「非软文」零基础学习TypeScript(源码开源)
今天,这篇文章篇幅很短,主要开放我最近学习整理TypeScript源码. 源码地址 https://github.com/maomincoding/typeScript_study 更多内容请见原文, ...
- 【react】什么是fiber?fiber解决了什么问题?从源码角度深入了解fiber运行机制与diff执行
壹 ❀ 引 我在[react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?一文中,介绍了虚拟dom的概念,以及react中虚拟dom的使用场景 ...
- Tomcat深入浅出(一)
一.Tomcat简介 我们下载好Tomcat后需要配置一下Java环境:如果打开出现闪退得情况,首先是jdk 同时配置JRE_HOME Tomcat的一些关键目录: /bin:存放用于启动及关闭的文件 ...
- Python 中生成器的原理
生成器的使用 在 Python 中,如果一个函数定义的内部使用了 yield 关键字,那么在执行函数的时候返回的是一个生成器,而不是常规函数的返回值. 我们先来看一个常规函数的定义,下面的函数 f() ...
- [Java学习笔记] Java异常机制(也许是全网最独特视角)
Java 异常机制(也许是全网最独特视角) 一.Java中的"异常"指什么 什么是异常 一句话简单理解:异常是程序运行中的一些异常或者错误. (纯字面意思) Error类 和 Ex ...
- Unique -「企划」新生守则(?
随想随记,主要是整活. 红色贝雷帽大爷会在校园不定期游走,遇见记得打招呼. 面食堂冰沙类饮品请快速解决或者边喝边搅,如果发现饮品甜度骤减请快速前往最近的垃圾桶扔掉. 关于散养猫小黄和小黑. 如果看见小 ...
- harbor之HTTPS安装
1.下载解压 # tar -xvf harbor-offline-installer-v1.7.6.tgz # cd /harbror 2.下载python2.7 # apt install pyth ...
- VGA设计(原理说明。Verilog代码实现,仿真结果)
各类显示屏的显示原理大部分是利用人眼的视觉暂留效应.比如之前的数码管显示就是设计每个周期内各个小段按顺序显示,来达到显示一个数字的效果. VGA同理,显示屏在显示时是一个像素一个像素地显示,在人眼看来 ...