【题解】USACO20FEB Swapity Swapity Swap S
有\(n\)头奶牛从左到右站成一排,初始第\(i\)个位置上为第\(i\)头奶牛。
每一次晨练包含\(m\)次翻转操作,翻转操作形如\((l_i,r_i)\),表示将区间\([l_i,r_i]\)的奶牛翻转。
现在要进行\(k\)次晨练,求之后每个位置上奶牛的编号。
\(n\le10^5,m\le100,k\le10^9\)。
Solution 1
设\(a_i\)表示位置\(i\)上的奶牛,不难发现我们可以从\(a_i=i\)开始做一次晨练,得到一个映射\(f(x)\)表原来在位置\(x\)上的牛经过一次晨练到达的位置。
那么我们要求的答案也就是\(f^k(x)\)。
这个形式不能不让人想到快速幂,而显然这个置换\(f(x)\)具有结合律,也就是说\(f^{2r}(x)=f^r(f^r(x))\),所以直接快速幂求解即可。
时间复杂度\(O(nm+n\log{k})\),代码在文末。
Solution 2
我们可以将\(k\)二进制拆分,然后通过倍增来解决这个问题。
设\(f_{i,j}\)表示做\(2^j\)次晨练后,第\(i\)个位置的奶牛编号。
则\(f_{i,j}=f_{f_{i,j-1}~,~j-1}\)。
对于每个位置,我们依次考虑\(k\)二进制拆分后的每一位,然后在\(f\)上面跳即可。
时间复杂度\(O(n\log{k})\),代码在文末。
Solution 3
我们发现Solution 1中的映射可以用图论模型来表示。
\(f_i\)表示经过一次晨练后,原来在位置\(i\)上的牛现在所在的位置。
那么对于位置\(i\),我们连一条\(i\rightarrow f_i\)的边。那么对于所有\(n\)个点,出度入度都为\(1\)。
现在我们在图上找环,显然每个点仅属于一个环。对于每个环内的点,我们可以通过取模运算计算出每个点的答案,即走\(k\)步到达的点。
时间复杂度\(O(nm+n)=O(nm)\),代码在文末。
Solution 4
由于Solution 3的瓶颈在于暴力翻转处理\(f\)数组,所以我们可以使用文艺平衡树进行区间翻转操作。
时间复杂度\(O(m\log{n}+n)\),代码在文末我没写。
代码
Solution 1
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int n,m,K,l[maxn],r[maxn],lp[maxn];
struct seq{
int a[maxn];
} a,f;
seq mul(seq x,seq y){//计算置换
seq res;
for(int i=1;i<=n;++i) res.a[i]=x.a[y.a[i]];
return res;
}
seq qpow(seq x,int k){
seq res=x;
while(k){
if(k&1) res=mul(res,f);
f=mul(f,f);
k>>=1;
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m>>K;
for(int i=1;i<=n;++i) a.a[i]=f.a[i]=i;
for(int i=1;i<=m;++i){
cin>>l[i]>>r[i];
reverse(f.a+l[i],f.a+r[i]+1);
}
seq ans=qpow(a,K);
for(int i=1;i<=n;++i) cout<<ans.a[i]<<"\n";
return 0;
}
Solution 2
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,m,K,a[N],f[N][31];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m>>K;
for(int i=1;i<=n;++i) a[i]=i;
for(int i=1,l,r;i<=m;++i){
cin>>l>>r;
reverse(a+l,a+r+1);
}
for(int i=1;i<=n;++i) f[i][0]=a[i];
for(int j=1;j<=30;++j){
for(int i=1;i<=n;++i)
f[i][j]=f[f[i][j-1]][j-1];
}
for(int i=1;i<=n;++i){
int p=i,r=K;
for(int j=30;j>=0;--j){
if((r>>j)&1){
r-=(1<<j);
p=f[p][j];
}
}
cout<<p<<"\n";
}
return 0;
}
Solution 3
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,m,K,a[N],f[N],ans[N];
bool vis[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m>>K;
for(int i=1;i<=n;++i) a[i]=f[i]=i;
for(int i=1,l,r;i<=m;++i){
cin>>l>>r;
reverse(a+l,a+r+1);
}
for(int i=1;i<=n;++i) f[a[i]]=i;
for(int i=1;i<=n;++i){
if(vis[i]) continue;
int p=f[i];
vector<int>v;
v.push_back(i);
while(p!=i){
v.push_back(p);
p=f[p];
}
int nxt=K%v.size();
for(int i=0;i<v.size();++i){
vis[v[i]]=1;
ans[v[nxt]]=v[i];
nxt=(nxt+1)%v.size();
}
}
for(int i=1;i<=n;++i) cout<<ans[i]<<"\n";
return 0;
}