题解 CF1515F【Phoenix and Earthquake】
CF1515F Phoenix and Earthquake
更好的阅读体验
下分场/ll
这道题其实很简单,考场降智想复杂写了个类似Brovuka的算法,然后被自己hack了/kk。
题意
给定一个
位置
分析
考虑猜一个结论:只要沥青总吨数
证明:考虑不断找到一条连接两个沥青总吨数大于等于
X 的边进行修建。 若已经修建了e 条边后无法找到下一条边,那么可知total-eX<X+(n-e-2)X (左边是当前沥青总吨数,右边是考虑任意一条边两端的沥青总吨数加上其他连通块的沥青总吨数)。 因此taotal<(n-1)X ,推出矛盾。
不难发现取该图的任意一个
我们在树上进行
由于每次可以直接把连接后儿子的沥青总吨数加到父亲上(没有连接的儿子不会影响父亲向上的连边),因此并不需要用并查集维护连通块的沥青总数。
时间复杂度:
代码
#include<stdio.h>
const int maxn=300005,maxm=maxn<<1;
int n,m,X,e,cnt1,cnt2;
int start[maxn],to[maxm],then[maxm],id[maxm],ans[maxn],a[maxn],vis[maxn];
long long sum;
inline void add(int x,int y,int i){
then[++e]=start[x],start[x]=e,to[e]=y,id[e]=i;
}
void dfs(int x){
vis[x]=1;
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(vis[y])
continue;
dfs(y);
if(a[x]+a[y]>=X)
a[x]+=a[y]-X,ans[++cnt1]=id[i];
else ans[--cnt2]=id[i];
}
}
int main(){
scanf("%d%d%d",&n,&m,&X);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),sum+=a[i];
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y,i),add(y,x,i);
}
if(sum<1ll*(n-1)*X){
puts("NO");
return 0;
}
puts("YES");
cnt1=0,cnt2=n;
dfs(1);
for(int i=1;i<=n-1;i++)
printf("%d\n",ans[i]);
return 0;
}