Harbour.Space Scholarship Contest 2021-2022 (open for everyone, rated, Div. 1 + Div. 2) E. Permutation Shift (思维)
-
题意:有一组排列\(a=[1,2,3,...,n]\),可以将排列的所有元素向右移动\(k\)个位置,然后选任意两个元素交换位置,最多交换\(m\)次,现在给你操作完成后的序列,问你有多少种可能的\(k\)值,使得原排列能得到现在的序列.
-
题解:将所有\(a[i]\)--,这样在每个元素移动\(k\)个位置后,得到\(a[i]=(i-k+n)\mod n\).那么我们就可以反推得到\(k\)的情况下序列\(p\)中有多少位置是合法的,即\(k=(i-a[i]+n)\mod n\),用桶\(cnt\)记录\(k\)每个\(k\)有多少合法的位置.因为移动\(k\)个位置后,最多交换\(m\)次,那么最多出现\(2*m\)个不合法的位置,所以\(cnt_k\ge n-2*m\).而\(m\le \frac{n}{3}\),\(cnt_k\ge \frac{n}{3}\).那么,在之前算\(k\)的合法位置的时候,一个合法的位置只能唯一对应一个\(k\),所以要满足\(cnt_k\ge \frac{n}{3}\),\(k\)最多只有三种情况.这样的话时间复杂度就能满足了,我们可以\(O(n)\)解决问题.
这里可以记一个板子,求最小交换次数的时候,将现序列的值\(b_i\)和原序列\(a_i\)连边,然后求连通块数\(cnt\),最后\(n-cnt\)就是最小交换次数.
int loop(vector<int> p,int n){ vector<bool> vis(n); int res=0; rep(i,0,n-1){ if(vis[i]) continue; int j=i; while(!vis[j]){ vis[j]=true; j=p[j]; } res++; } return n-res; }
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int loop(vector<int> p,int n){ vector<bool> vis(n); int res=0; rep(i,0,n-1){ if(vis[i]) continue; int j=i; while(!vis[j]){ vis[j]=true; j=p[j]; } res++; } return n-res; } bool check(int n,int m,int k,vector<int> p){ vector<int> q; rep(i,k,n-1) q.pb(p[i]); rep(i,0,k-1) q.pb(p[i]); return loop(q,n)<=m; } int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); int _; cin>>_; while(_--){ int n,m; cin>>n>>m; vector<int> a(n); rep(i,0,n-1) cin>>a[i]; //a[i]-1=(i-k+n) mod n vector<int> cnt(n); //记录k情况下,合法的位置数 rep(i,0,n-1){ a[i]--; cnt[(i-a[i]+n)%n]++; //反推出k,因为每个a[i]在位置i上只会对应一种k的合法情况 } vector<int> ans; rep(i,0,n-1){ //枚举k if(cnt[i]>=n-2*m && check(n,m,i,a)){ ans.pb(i); } } cout<<(int)ans.size()<<' '; for(auto w:ans) cout<<w<<' '; cout<<'\n'; } return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮