Harbour.Space Scholarship Contest 2021-2022 Permutation Shift
Posted thusloop
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Harbour.Space Scholarship Contest 2021-2022 Permutation Shift相关的知识,希望对你有一定的参考价值。
思路:下表从0开始,之后a[i]-- 方便操作 {2,3,4,1} -->{1,2,3,0}
well-known problem :
若排列数组a通过交换变为排列数组b 需要建图 在 a[i]与b[i] 之间建立一条无向边 需要交换的次数为 n-c(c为联通分支数)
比如 a={1,2,0,3} b= {1,2,3,0}
则 1–1 2–2 0—3 建边 联通分支为c=3 则需要交换 4-3 =1 次(可用并查集实现)
位移k次后 p[i]=(i-k+n)%n;
k=0 {0,1,2,3}
k=1 {3,0,1,2}
k=2 {2,3,0,1}
k=3 {1,2,3,0}
要使得 原数组a 能变成 k次位移后的数组b 则需 可用的交换次数 m>= n-c
优化 :m<=n/3。 m次交换 最多使得2m个数不在原来位子 cntk>=n-2m (cntk为k次位移后与a相同位子数值相同的个数)
可以求出a每个位子是经过几次位移后得到的 即:(i-k+n)%n=a[i] --> k+ny=i-a[i] (我用了exgcd求解k)
#include<bits/stdc++.h>
#define int long long
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int inf=2e18+100;
const int maxn=3e5+100;
int a[maxn],b[maxn];
int pre[maxn];
int n,m;
int find(int x)
{
if(x!=pre[x])pre[x]=find(pre[x]);
return pre[x];
}
void join(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx!=fy)
{
pre[fx]=fy;
}
}
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int res=exgcd(b,a%b,y,x);
y-=a/b*x;
return res;
}
map<int,int>mp;
signed main()
{
IOS
int tt;
cin>>tt;
while(tt--)
{
mp.clear();
cin>>n>>m;
for(int i=0; i<n; i++)
{
cin>>a[i];
pre[i]=i;
a[i]--;
}
for(int i=0;i<n;i++)
{
int x;int y;
int gcd=exgcd(1,n,x,y);
x=x*(i-a[i])/gcd;
x=(x+n/gcd)%(n/gcd);
mp[x]++;
}
int cnt=0;
for(auto it:mp)
{
if(it.second>=n-2*m)
{
int k=it.first;
for(int i=0;i<n;i++)
{
join(a[i],(i-k+n)%n);
}
int sum=0;
for(int i=0;i<n;i++)
{
if(pre[i]==i)sum++;
pre[i]=i;
}
if(m>=n-sum)b[++cnt]=k;
}
}
cout<<cnt;
for(int i=1;i<=cnt;i++)
{
cout<<" "<<b[i];
}
cout<<"\\n";
}
}
以上是关于Harbour.Space Scholarship Contest 2021-2022 Permutation Shift的主要内容,如果未能解决你的问题,请参考以下文章
Harbour.Space Scholarship Contest 2021-2022 (open for everyone, rated, Div. 1 + Div. 2) D题解
Harbour.Space Scholarship Contest 2021-2022 (open for everyone, rated, Div. 1 + Div. 2)(补题)