置换环知识点

F. Shifting String #797div3 

Problem - F - Codeforces

题意是给你一个字符串和一个排列,s1s2s3s4...变成sp1,sp2,sp3...

比如abcd 4 2 1 3=>dbac

问你至少操作多少次可以变成原来的字符串

这题一看就是周期问题,然后我直接上手暴力,不出意外的tle了hh

不要那么一根筋啊,要多想想的

仔细揣摩可以发现 如果像是ab 21的话,会一直变成ba ab...这样子循环,那这对循环与其他字母怎么循环无关,这样命名一下为一个环。然后整个序列里面可以找到若干个这样的环,不管怎么循环,它们循环自己的与其他无关。这样的话,整个序列的周期就是所有环的lcm(最小公倍数)

注意一点:环的长度!=这个环的周期 刚开始我怎么也没想出来

把环求出来还需要求一下这个环的周期

周期怎么求:最长不可能超过这个环的长度,因为最坏的情况是全部循环一遍,直接上个公式吧:s[(a+k)%cnt]==s[a]如果每个字母都可以满足这个条件,说明循环一遍之后还是它,从小到大枚举满足条件的就是最小周期

周期还有一种求法,一位大佬提醒用kmp可以把时间复杂度优化到O(n),我不会QAQ...

(其实还有一种方法,我不会映射而已...QAQ)

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define IOS ios::sync_with_stdio(false), cin.tie(0);
#include<iostream>
#include<map>
#include<set>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#include<queue>
#include<deque>
using namespace std;
typedef long long ll;
typedef pair<int,int> PAII;
const int N=2e6+10,M=5050,INF=0x3f3f3f3f,mod=998244353;
int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}
int p[N];
bool st[N];
char ch[N],s[N];
int main(){
    IOS; 
    int T;
    //T=1;
    cin>>T;
    while(T--)
    {   
		int n;
		cin>>n;
		cin>>ch+1;
		for(int i=1;i<=n;i++)
		{
			cin>>p[i];
			st[i]=false;
		}
		ll res=1;
		for(int i=1;i<=n;i++)
		{
			if(!st[i])
			{
				int cnt=0;
				for(int j=i;!st[j];st[j]=true,j=p[j]) s[cnt++]=ch[j];//找到环**
				ll t;
				for(int k=1;k<=cnt;k++)//找周期 周期最多是长度 
				{
					if(cnt%k==0)
					{
						int f=1;
						for(int z=0;z<cnt;z++)
						{
							if(s[(z+k)%cnt]!=s[z])
							{
								f=0;
								break;
							}
						}
						if(f)
						{
							t=k;
							break;
						}
					}
				}
				res=res/__gcd(t,res)*t;
			}
		}
		cout<<res<<"\n";
    }
    return 0;
}
/*



*/

多积累题目经验吧.

******************************************************

Problem - C - Codeforces

题意是给你一个排列a,一个排列b,给一个序列c,如果c等于0,从ai或者bi任意选一个数 保证ci一定为ai bi中的任意一个,问有多少种这样的方案,要保证c是个排列

这个题也是和环有关的,予以并查集进行实现

因为你会发现 如果有一个值你知道了,接下来的一连串的数你可能都确定了

所以这就是个突破口

可以把ab序列划分成若干个环,环的性质是只要你确定了其中的任何一个数,那整个环的其他数字你全都知道了。所以在找到环的时候,你还需要判断一下这个环里面有没有确定的数字,如果有,那很好,这个环的贡献是1,(其实由于乘法原理,这个1是乘的1可以不用管),如果没有,那这个环的贡献就是2,很好证,其实就是一个位置有两种选择,每选一种其他数字都确定进而全部的环的选择也就是贡献为2。但是这里注意一下,如果某个环的长度是1,那贡献也是1

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define IOS ios::sync_with_stdio(false), cin.tie(0);
#include<iostream>
#include<map>
#include<set>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#include<queue>
#include<deque>
using namespace std;
typedef long long ll;
typedef pair<int,int> PAII;
const int N=2e6+10,M=5050,INF=0x3f3f3f3f,mod=1e9+7;
int a[N],b[N],c[N],p[N];
bool st[N],s[N];
int main(){
    //IOS; 
    int T;
    //T=1;
    cin>>T;
    while(T--)
    {   
		int n;
		cin>>n;
		for(int i=1;i<=n;i++) st[i]=false,s[i]=false;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++) cin>>b[i];
		for(int i=1;i<=n;i++)
		{
			cin>>c[i];
			if(c[i])
			{
				st[a[i]]=true;
				st[b[i]]=true;
			}
		}
		for(int i=1;i<=n;i++) p[a[i]]=b[i];
		ll res=1; 
		for(int i=1;i<=n;i++)
		{
			if(!s[i])
			{
				int f=0,cnt=0;
				for(int j=i;!s[j];j=p[j])
				{
					s[j]=true;
					cnt++;
					f=f|st[j];
				}
				if(cnt!=1&&!f) res=res*2%mod;
			}
		}
		cout<<res<<"\n"; 
    }
    return 0;
}
/*



*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值