【题解】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;
}
posted @ 2021-12-23 21:07  hzy1  阅读(84)  评论(0编辑  收藏  举报