题解:AT_agc060_e [AGC060E] Number of Cycles
[AGC060E] Number of Cycles
设
f(a) 表示排列a 中的置换环数量,给定一个1\sim n 的排列p 和正整数k ,构造一个1\sim n 的排列x 满足f(x)+f(x\circ p) = k 。或者报告无解。$n\le 2\times 10^5
给出一个比较简洁的
我们考虑用调整法,设
考虑求出
那么如果
依次枚举
- 如果
y_{b_i},y_{b_{i-1}} 不属于一个环,那么交换(x_{b_1},x_{b_i}),(y_{b_1},y_{b_i}) ,lst 不变,并将s 减2 。 - 如果
y_{b_i},y_{b_{i-1}} 属于一个环,并且lst\ne 0 ,那么就交换(x_{lst},x_{b_i}),(y_{lst},y_{b_i}) ,将lst 赋为0 ,s 减2 。 - 如果
y_{b_i},y_{b_{i-1}} 属于一个环,并且lst=0 ,那么就交换(x_{b_1},x_{b_i}),(y_{b_1},y_{b_i}) ,将lst 赋为b_i ,s 不变。
可以发现,当
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int N = 2e5+5;
int a[N],b[N],c[N],id[N],n,k;
char buf[1<<21],*p1,*p2;
inline int rd()
{
char c;int f = 1;
while(!isdigit(c = getchar()))if(c=='-')f = -1;
int x = c-'0';
while(isdigit(c = getchar()))x = x*10+(c^48);
return x*f;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
for(int t = rd();t--;)
{
n = rd();k = rd();int tot = 0,cnt = n,lst = 0;
for(int i = 1;i <= n;i++)a[i] = rd(),b[i] = i,id[i] = 0;
for(int i = 1;i <= n;i++)if(!id[i])
{
cnt++;
for(int x = i;!id[x];x = a[x])c[++tot] = x,id[x] = cnt;
}
if(k > cnt||k%2 != cnt%2){puts("No");continue;}
for(int i = 2;i <= n&&cnt > k;i++)
if(id[c[i]] != id[c[i-1]])swap(b[c[1]],b[c[i]]),cnt -= 2;
else if(lst)swap(b[lst],b[c[i]]),lst = 0,cnt -= 2;
else lst = c[i],swap(b[c[1]],b[c[i]]);
puts("Yes");
for(int i = 1;i <= n;i++)printf("%d%c",b[i]," \n"[i==n]);
}
return 0;
}