Link.
Codeforces
Luogu
Thanks To Lillia
P.S.
以下推理过程都是看完题解之后回想的。
Description.
给定一个图,每条边可以有 \(0\) 或 \(1\) 或 \(2\) 个金币,每个点可以有 \(0\) 或 \(1\) 个金币。
每个点的权值定义为它和它所有连边总金币个数,现在要让任意一条边两个端点的权值不同。
Coding.
首先考虑无解情况 假装没看到题目中没给无解情况输出
发现必定有解。
粗略证明:精确证明的话直接构造出来就证完了
假设当前的点为 \(x\),它的度数为 \(\deg(x)\)。
首先,我们考虑当前这个点有几种不同的权值。
必定是 \(2\deg(x)+2\) 种,因为最小是 \(0\) 最大是 \(2\deg(x)+1\)
然后,我们考虑和他相邻的点至多有几种不同数。
发现至多只有 \(\deg(x)\)。
所以我们从理论上证明了最后应该是有可行解的。
然后我们考虑,上面的粗略证明中,界限放的很宽。
种类数有 \(2\deg(x)\) 而不同的只有 \(\deg(x)\),考虑这个 \(2\) 有什么用。
同时,如果没有这个 \(2\),我们仍然满足,因为 \(\deg(x)+2>\deg(x)+1\)。
所以我们可以利用这个 \(2\),具体的,我们首先先把所有边权值赋成 \(1\)。
然后,对于当前所需要构造的点,我们可以对它出边和出边指向的点一个 \(+1\) 一个 \(-1\),我们就可以在其他点权值不变的情况下把当前这个点任意 \(+1\) 和 \(-1\)。
同时,考虑通过上面这种方法能得到的所有点总数是 \(\deg(x)+1\) 个,严格大于它不能等于的数。
所以必定存在一个数和周围全都不一样,让当前这个点权值为这个数即可。
总复杂度是 \(O(\sum_{i=1}^ndeg(i))=O(m)\) 的。
实现细节:因为是双向边,我们可以强行定向,钦定边是从 \(i\) 指向 \(j\) 的 \((i<j)\)。
Coding.
点击查看弱小代码//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),bz=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) bz=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
bz?x=-x:x;
}/*}}}*/
struct edge{int to,id,nxt;}e[2000005];int n,m,et,head[12505],id[12505],nx[12505];
int fr[1000005],tw[1000005],we[1000005],wp[12505],vl[12505];char vs[1012505];
inline void adde(int x,int y,int id) {e[++et]=(edge){y,id,head[x]},head[x]=et;}
int main()
{
read(n),read(m);for(int i=1;i<=m;i++) {read(fr[i]),read(tw[i]);if(fr[i]<tw[i]) swap(fr[i],tw[i]);}
for(int i=1;i<=m;i++) adde(fr[i],tw[i],i),we[i]=1,vl[fr[i]]++,vl[tw[i]]++;
for(int x=1;x<=n;x++)
{
for(int i=head[x];i;i=e[i].nxt) if(!wp[e[i].to]) wp[e[i].to]++,we[e[i].id]--,vl[x]--;
for(int i=head[x];i;i=e[i].nxt) vs[vl[e[i].to]]=1;
for(int i=head[x];i;i=e[i].nxt) if(vs[vl[x]]) vl[x]++,wp[e[i].to]--,we[e[i].id]++;else break;
for(int i=head[x];i;i=e[i].nxt) vs[vl[e[i].to]]=0;
}
int cn=0;for(int i=1;i<=n;i++) cn+=wp[i];
printf("%d\n",cn);for(int i=1;i<=n;i++) if(wp[i]) printf("%d ",i);
putchar('\n');for(int i=1;i<=m;i++) printf("%d %d %d\n",fr[i],tw[i],we[i]);
return 0;
}