题解 CF1530D 【Secret Santa】

这道题挺有意思的。

首先答案为 \(b_i\) 中不同数字的个数,现在我们来考虑如何构造这个东西。

首先,我们把图建出来,每个点连向它要送礼物的人,我们会发现最终的答案中这个图由若干个环组成,切环的大小不能为 \(1\)

那么,我们可以让对于每个点 \(i\) 任意一个在题目中想送 \(i\) 礼物的人向 \(i\) 连边,这样答案就保证了。

我们考虑剩下的,我们让入度为 \(1\),出度为 \(0\) 的点,连向它所在的链的最后方构成环,由于入度和出度都不超过 \(1\),所有非常好维护。

还有的点是入度和出度都为 \(0\) 的。如果这样的点数量为 \(0\) 不需要任何操作;如果这样的点数量为 \(>1\),则连成一个环;如果数量为 \(1\),我们就让这个点强行加入这个图中。

#include<bits/stdc++.h>
#define log(a) cerr<<"\033[32m[DEBUG] "<<#a<<'='<<(a)<<" @ line "<<__LINE__<<"\033[0m"<<endl
#define LL long long
#define SZ(x) ((int)x.size()-1)
#define ms(a,b) memset(a,b,sizeof a)
#define F(i,a,b) for(int i=(a);i<=(b);++i)
#define DF(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
inline int read(){char ch=getchar(); int w=1,c=0;
	for(;!isdigit(ch);ch=getchar()) if (ch=='-') w=-1;
	for(;isdigit(ch);ch=getchar()) c=(c<<1)+(c<<3)+(ch^48);
	return w*c;
}
const int N=2e5+10;
int a[N],ans[N],h[N],l[N];
int find(int x){
	if(!h[x])return x;
	return h[x]=find(h[x]);
}
signed main(){
	int _=read();
	while(_--){
		int n=read(),s=0;
		vector<int>v;
		F(i,1,n)h[i]=ans[i]=0;
		F(i,1,n){
			a[i]=read();
			if(!h[a[i]]){
				s++;
				h[a[i]]=l[a[i]]=i;
				ans[i]=a[i];
			}
		}
		F(i,1,n)
			if(!ans[i]){
				if(find(i)!=i)ans[i]=find(i);
				else v.push_back(i);
			}
		if(v.size()==1){
			// log(v[0]);
			ans[v[0]]=a[v[0]];
			ans[l[a[v[0]]]]=v[0];
		}else{
			if(v.size()){
				F(i,0,SZ(v)-1)ans[v[i]]=v[i+1];
				ans[v[SZ(v)]]=v[0];
			}
		}
		cout<<s<<endl;
		F(i,1,n)cout<<ans[i]<<" ";
		cout<<endl;
	}
	return 0;
}
posted @ 2021-08-16 20:52  zhaohaikun  阅读(94)  评论(0编辑  收藏  举报