一道思维题。
题目没有说无解输出 (-1),这意味着对于任意 (G) 一定存在一个合法的排列方案。因此可以考虑采用归纳法。对于一个点的情况显然成立,重点在于如何从 (n-1) 个点推到 (n) 个点。
然后就是我所想不到的地方了。考虑与第 (n) 个点相邻的点集 (S),我们先在第 (n) 个点与 (S) 相连的边上都放上一枚硬币,这样不过这样可能会不合法,因此我们需要调整。注意,由于是归纳,因此我们需要在不改变 (1sim n-1) 号点的权值的情况下尝试调整 (n) 的权值,否则就会导致 (1sim n-1) 不合法,也就是归纳前提不成立了。
进一步分析这个模型,注意到对于一个点 (xin T) 有两种方法在不改变 (x) 的权值的前提下改变 (n) 的权值:
- 在 (x) 与 (n) 的边上拿走一个硬币,在 (x) 上放上一个硬币。
- 在 (x) 与 (n) 的边上放上一个硬币,在 (x) 上拿走一个硬币。
我们考虑这样的算法,对于所有 (xin S) 且 (x) 上面没有硬币,我们对 (x) 进行一遍操作 (1),这样可以确保所有 (S) 中的硬币都可以进行操作 (2)。然后设现在 (n) 的权值为 (v),那么我们显然可以通过 (2) 操作将 (n) 的权值变为 ([v,v+|S|]) 中的任意一个数。我们希望 (n) 的权值与 (S) 中点的权值都不同,而 (S) 中的点最多只有 (|S|) 个权值,因此总能变成一个不在 (|S|) 中的权值,得证。
代码异常好写:
u1s1 这种思维题就是题解写起来容易,想起来死活想不到……
const int MAXN=1.25e4;
const int MAXM=1e6;
int n,m,u[MAXM+5],v[MAXM+5],val[MAXN+5],w[MAXM+5],is[MAXN+5];
vector<pii> g[MAXN+5];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;w[i]=1,i++){
scanf("%d%d",&u[i],&v[i]);
g[u[i]].pb(mp(v[i],i));
g[v[i]].pb(mp(u[i],i));
val[u[i]]++;val[v[i]]++;
}
for(int i=1;i<=n;i++){
queue<pii> q;set<int> st;
for(pii p:g[i]){
if(p.fi>i) continue;
if(!is[p.fi]) w[p.se]--,is[p.fi]++,val[i]--;
q.push(p);st.insert(val[p.fi]);
}
while(st.find(val[i])!=st.end()){
pii p=q.front();q.pop();
w[p.se]++;is[p.fi]--;val[i]++;
}
} vector<int> resv;
for(int i=1;i<=n;i++) if(is[i]) resv.pb(i);printf("%u
",resv.size());
for(int i=0;i<resv.size();i++) printf("%d%c",resv[i],"
"[i+1==resv.size()]);
for(int i=1;i<=m;i++) printf("%d %d %d
",u[i],v[i],w[i]);
return 0;
}