题面

在紧张又忙碌地准备联合省选时,发生了大地震,把原本要参赛的

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(图论推导,构造)的更多相关文章

  1. Luogu3524 POI2011 Party 图论、构造

    题目传送门:https://www.luogu.org/problemnew/show/P3524 大意:给一个$N$个点的图,其中一定有一个大小为$\frac{2}{3}N$的团,程序需给出一个大小 ...

  2. Gym - 101503I 利用到图论的构造

    比赛的时候没有注意到 给出的up矩阵 能使我们随便选一列 确定这一列的rank 这样我们得出每一行列的rank 进行构图 大->小 然后从大到小放 当前放的点 和他有因果关系并且比他大的点必须已 ...

  3. [SDOI2019]热闹又尴尬的聚会(图论+set+构造)

    据说原数据可以让复杂度不满的暴力O(Tn^2)过掉……O(Tn^2)方法类似于codeforces一场div2的E题 有一种比较好的方法:每次找出原图G中度最小的点加入q,然后将相邻的点加入新图G'. ...

  4. python全栈开发 * 14 知识点汇总 * 180530

    14 生成器表达式 内置函数# 一.迭代器 (补充)# 1.如何判断迭代对象,迭代器# (1).dir(obj)检测对象有没有iter方法,然后it=obj.__iter__() 获取迭代器 , it ...

  5. 函数式JS: 原来promise是这样的monad

    转载请注明出处: http://hai.li/2017/03/27/prom... 背景 上篇文章 函数式JS: 一种continuation monad推导 得到了一个类似promise的链式调用, ...

  6. CF97C Winning Strategy 构造、图论

    题目传送门:http://codeforces.com/problemset/problem/97/C 题意:给出$n$与一个范围在$[0,1]$内的递增序列$P_0-P_n$,试构造一个无穷序列$\ ...

  7. CCO2017 Vera and Trail Building 构造+图论

    正解:构造+图论 解题报告: 找了半天才找到的传送门! 先简要表达下题意 一个图上,如果存在(a,b)满足a<b且存在从a到b再回到a的路径,每条道路被经过至多一次,我们称(a,b)为完美点对试 ...

  8. AES128加密-S盒和逆S盒构造推导及代码实现

    文档引用了<密码编码学与网络安全--原理和实践>里边的推导过程,如有不妥,请与我联系修改. 文档<FIPS 197>高级加密标准AES,里边有个S盒构造,涉及到了数论和有限域的 ...

  9. HIT手 | 机械电气构造简述和微分运动学及静力学的简单推导

      机械结构电气构造简述 HIT手有四个手指,每个手指4个关节,其中第一和第二个关节正交,第三和第四个关节机械耦合,故只有3个自由度,另外大拇指多了一个相对手掌运动的自由度,故一只手掌总共有13各个自 ...

随机推荐

  1. 如何优雅的使用MyBatis?

    ​本文目录 什么是 MyBatis ? 映射器(mappers) typeAliases 类型别名减少类完全限制名的冗余 处理枚举类型 多行插入 重用 SQL 代码段,消除重复 字符串替换#{}和${ ...

  2. 20天等待,申请终于通过,安装和体验IntelliJ IDEA新UI预览版

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于IDEA的预览版 IDEA会启用新的UI,这事情之 ...

  3. 「非软文」零基础学习TypeScript(源码开源)

    今天,这篇文章篇幅很短,主要开放我最近学习整理TypeScript源码. 源码地址 https://github.com/maomincoding/typeScript_study 更多内容请见原文, ...

  4. 【react】什么是fiber?fiber解决了什么问题?从源码角度深入了解fiber运行机制与diff执行

    壹 ❀ 引 我在[react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?一文中,介绍了虚拟dom的概念,以及react中虚拟dom的使用场景 ...

  5. Tomcat深入浅出(一)

    一.Tomcat简介 我们下载好Tomcat后需要配置一下Java环境:如果打开出现闪退得情况,首先是jdk 同时配置JRE_HOME Tomcat的一些关键目录: /bin:存放用于启动及关闭的文件 ...

  6. Python 中生成器的原理

    生成器的使用 在 Python 中,如果一个函数定义的内部使用了 yield 关键字,那么在执行函数的时候返回的是一个生成器,而不是常规函数的返回值. 我们先来看一个常规函数的定义,下面的函数 f() ...

  7. [Java学习笔记] Java异常机制(也许是全网最独特视角)

    Java 异常机制(也许是全网最独特视角) 一.Java中的"异常"指什么 什么是异常 一句话简单理解:异常是程序运行中的一些异常或者错误. (纯字面意思) Error类 和 Ex ...

  8. Unique -「企划」新生守则(?

    随想随记,主要是整活. 红色贝雷帽大爷会在校园不定期游走,遇见记得打招呼. 面食堂冰沙类饮品请快速解决或者边喝边搅,如果发现饮品甜度骤减请快速前往最近的垃圾桶扔掉. 关于散养猫小黄和小黑. 如果看见小 ...

  9. harbor之HTTPS安装

    1.下载解压 # tar -xvf harbor-offline-installer-v1.7.6.tgz # cd /harbror 2.下载python2.7 # apt install pyth ...

  10. VGA设计(原理说明。Verilog代码实现,仿真结果)

    各类显示屏的显示原理大部分是利用人眼的视觉暂留效应.比如之前的数码管显示就是设计每个周期内各个小段按顺序显示,来达到显示一个数字的效果. VGA同理,显示屏在显示时是一个像素一个像素地显示,在人眼看来 ...