CF1515F Phoenix and Earthquake
證明題。
思路
考慮不合法的情況,如果 \(\sum a_i < (n-1)\times x\),肯定是不合法的。
再考慮對於一個可行的情況,最後縮的邊肯定形成一棵樹,所以我們大膽假設:任意一棵生成樹只要滿足 \(\sum a_i \geq (n-1)\times x\) 有合法的縮邊方案。
考慮歸納證明:
在 \(n\) 個點的樹中考慮葉子節點 \(u\),其父親為 \(v\)。
-
若 \(a_u < x\),那麼我們刪除 \((u,v)\) 這條邊,由於所需總和 \((n-1)\times x\) 減少了 \(x\),而 \(\sum a_i\) 減少了 \(a_u\),所以歸納假設任然成立。將邊 \((u,v)\) 加入棧。
-
若 \(a_u \geq x\),那麼我們把 \(a_u\) 和 \(a_v\) 進行縮點,原樹轉化為 \(n-1\) 個節點新樹。將邊 \((u,v)\) 加入佇列。
先使用佇列輸出邊,再輸出棧中的邊
再給出這樣的輸出邊的方案的正確性證明:
首先使用條件 \(2\) 中的邊進行縮點,假設縮剩下了 \(t\) 個點,第 \(i\) 個點的點權為 \(a_i\)。
此時有 \(\sum a_i \geq (t-1)\times x\)。
其中僅可能包括根節點的 \(a_i\) 大於 \(x\);否則,可以進一步進行 \(2\) 操作的縮點,將滿足 \(a_i\geq x\) 的點縮到根節點處。
當 \(i>1\) 時 \(a_i < x\),即 \(i>1\) 時 \(x-a_i \geq 1\)。
不妨設根節點的縮點點權為 \(a_1\),有 \(a_1 \geq \sum_{i=2}^t (x-a_i)\)。
此時根節點可以與任意一個相連的縮點合併,不妨設該點為 \(t\)(\(a_1\) 肯定大於等於 \(x-a_t\)),相當於不等式兩邊同時加了 \(a_t-x\),不等式仍然成立。
每次縮根節點和其相連的點就可以嘍。
而我們根據棧輸出的邊正滿足這樣的條件,證明留給讀者自行思考(doge)。
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define F first
#define S second
const int maxn=3e5+5;
int n,m,x;
ll a[maxn];
vector<pii>E[maxn];
bool vis[maxn];
queue<int>que;
stack<int>stk;
inline void dfs(int u)
{
vis[u]=true;
for(auto v:E[u])
{
if(vis[v.F]) continue;
dfs(v.F);
if(a[v.F]>=x) que.push(v.S),a[u]+=a[v.F]-x;
else stk.push(v.S);
}
}
int main()
{
ll sum=0;
scanf("%d%d%d",&n,&m,&x);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum+=a[i];
if(sum<1ll*(n-1)*x){printf("NO");return 0;}
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
E[u].push_back({v,i}),E[v].push_back({u,i});
}
dfs(1);
puts("YES");
while(!que.empty()) printf("%d\n",que.front()),que.pop();
while(!stk.empty()) printf("%d\n",stk.top()),stk.pop();
}