[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各个自 ...
随机推荐
- C语言 - 基础数据结构和算法 - 单向链表
听黑马程序员教程<基础数据结构和算法 (C版本)>,照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友可 ...
- Kubernetes将弃用Docker!与 containerd容器引擎
时间戳:2022-06-07 20:32:19 星期二 撰写文档参考:(阿良-腾讯课堂)Kubernetes将弃用Docker 参考博客k8s入坑之路(3)containerd容器 container ...
- Java开发学习(五)----bean的生命周期
一.什么是生命周期 首先理解下什么是生命周期? 从创建到消亡的完整过程,例如人从出生到死亡的整个过程就是一个生命周期. bean生命周期是什么? bean对象从创建到销毁的整体过程. bean生命周期 ...
- 强化学习-linux安装gym、atari和box2d环境
安装gym和atari环境 pip3 install gym pip3 install gym[atari] pip3 install gym[accept-rom-license] 安装box2d环 ...
- 用python这样做,offer还不是拿到手软?
大家好鸭,我是小熊猫 本篇代码提供者: 自游老师 老师简介:青灯教育金牌讲师3年Python爬虫开发经验七年在线教育经验擅长Python.c等语言曾任职多家互联网公司爬虫工程师.Python讲师 [环 ...
- 面向个性化需求的在线云数据库混合调优系统 | SIGMOD 2022入选论文解读
SIGMOD 数据管理国际会议是数据库领域具有最高学术地位的国际性会议,位列数据库方向顶级会议之首.近日,腾讯云数据库团队的最新研究成果入选 SIGMOD 2022 Research Full Pap ...
- 抓到 Netty 一个 Bug,顺带来透彻地聊一下 Netty 是如何高效接收网络连接的
本系列Netty源码解析文章基于 4.1.56.Final版本 对于一个高性能网络通讯框架来说,最最重要也是最核心的工作就是如何高效的接收客户端连接,这就好比我们开了一个饭店,那么迎接客人就是饭店最重 ...
- Qucs初步使用指南(不是multism)
众所周知,Multism是一款强大的电路仿真软件,学习电子电路的同学都会接触到. 但是,这软件不支持Linux.(这就很魂淡了啊) 我的主力机是Linux,不能进行电路仿真成了学习的最大障碍. 使用w ...
- C++系统函数
C++语言预先编写了很多常用函数提供给广大程序员使用,这些函数被统称为系统函数.C++语言全盘继承了C语言的标准C库,另外又增加了一些新的库(更多的是系统类库),这些新库被统称为C++标准库. 一.C ...
- Unity-2D像素晶格化消融
效果展示: ShaderLab Shader功能:图像变白+根据顶点的y值作透明裁剪: 才是可操作属性: IsDead: 控制像素变白,片元着色阶段IsDead小于0将颜色改为白色: Percent: ...