题解 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;
}