Codeforces 1423N - BubbleSquare Tokens(归纳+构造)

Codeforces 题目传送门 & 洛谷题目传送门

一道思维题。

题目没有说无解输出 \(-1\),这意味着对于任意 \(G\) 一定存在一个合法的排列方案。因此可以考虑采用归纳法。对于一个点的情况显然成立,重点在于如何从 \(n-1\) 个点推到 \(n\) 个点。

然后就是我所想不到的地方了。考虑与第 \(n\) 个点相邻的点集 \(S\),我们先在第 \(n\) 个点与 \(S\) 相连的边上都放上一枚硬币,这样不过这样可能会不合法,因此我们需要调整。注意,由于是归纳,因此我们需要在不改变 \(1\sim n-1\) 号点的权值的情况下尝试调整 \(n\) 的权值,否则就会导致 \(1\sim n-1\) 不合法,也就是归纳前提不成立了。

进一步分析这个模型,注意到对于一个点 \(x\in T\) 有两种方法在不改变 \(x\) 的权值的前提下改变 \(n\) 的权值:

  1. \(x\)\(n\) 的边上拿走一个硬币,在 \(x\) 上放上一个硬币。
  2. \(x\)\(n\) 的边上放上一个硬币,在 \(x\) 上拿走一个硬币。

我们考虑这样的算法,对于所有 \(x\in 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\n",resv.size());
	for(int i=0;i<resv.size();i++) printf("%d%c",resv[i]," \n"[i+1==resv.size()]);
	for(int i=1;i<=m;i++) printf("%d %d %d\n",u[i],v[i],w[i]);
	return 0;
}
posted @ 2021-06-02 17:47  tzc_wk  阅读(47)  评论(0编辑  收藏  举报