Phoenix and Earthquake
题解
挺水的一道题。
首先我们要想明白如何判断一个图是否能行。
首先,如果不是联通图或者
∑
i
=
1
n
a
i
<
(
n
−
1
)
x
\sum_{i=1}^{n}a_{i}< (n-1)x
∑i=1nai<(n−1)x是肯定不行的,因为它合并都合并不了
n
−
1
n-1
n−1条边。
否则,它是一定能行的。因为这样必定有一条边的两端和是大于
x
x
x,那么这条边就一定可以被合并掉。然后,我们得到的是一个
n
−
1
n-1
n−1个点,且
∑
i
=
1
n
a
i
⩾
(
n
−
2
)
x
\sum_{i=1}^{n}a_{i}\geqslant (n-2)x
∑i=1nai⩾(n−2)x的图,这个图依旧可以重复以下过程。
证明了可行性,接下来我们操作呢?
首先,如果有一个权值不小于
x
x
x的点,我们可以直接将它与其它点合并,它与任意一个点合并都是可行的。
否则我们可以证明现当每个点都小于
x
x
x时,每一条边的和都是不小于
x
x
x的。
如果有一条边满足的
a
u
+
a
v
<
x
a_{u}+a_{v}< x
au+av<x,则其它
n
−
2
n-2
n−2个点都取
x
−
1
x-1
x−1,总和也达不到
(
n
−
1
)
x
(n-1)x
(n−1)x,也就是不满足我们最开始的条件,这当然是不可能的。
所以这样我们只需要随便合并一条边就可以了。
具体实现过程中,我们只需要每次对权值最大的点找一条边合并即可。
时间复杂度 O ( m l o g n ) O\left(mlog\,n\right) O(mlogn)。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 300005
#define lowbit(x) (x&-x)
#define reg register
#define mp make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
typedef unsigned int uint;
const int INF=0x7f7f7f7f;
const int jzm=233;
const int mo=998244353;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,m,head[MAXN],tot,fa[MAXN],last[MAXN];LL val[MAXN],sum,x;
struct edge{int to,nxt;}e[MAXN<<1];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;if(!last[u])last[u]=tot;}
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return x==fa[x]?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){int u=findSet(a),v=findSet(b);if(u!=v)fa[u]=v;}
struct ming{LL val;int id;friend bool operator < (const ming &x,const ming &y){return x.val<y.val;}};
priority_queue<ming> q;
int main(){
read(n);read(m);read(x);makeSet(n);
for(int i=1;i<=n;i++)read(val[i]),sum+=val[i];
for(int i=1,u,v;i<=m;i++)read(u),read(v),addEdge(u,v),addEdge(v,u);
if(sum<1ll*(n-1)*x){puts("NO");return 0;}puts("YES");
for(int i=1;i<=n;i++)q.push((ming){val[i],i});sum=0;
while(!q.empty()&&sum<n-1){
int t=q.top().id;q.pop();if(t!=findSet(t))continue;
//printf("match %d\n",t);
for(int i=head[t];i;i=e[i].nxt){
int v=findSet(e[i].to);head[t]=e[i].nxt;if(t==v)continue;
val[t]=val[t]+val[v]-x;val[v]=0;fa[v]=t;q.push((ming){val[t],t});
printf("%d\n",(i+1)/2);(i==last[t]?head[t]:e[last[t]].nxt)=head[v];last[t]=last[v];sum++;break;
}
}
puts("");
return 0;
}