https://vjudge.net/problem/CodeForces-1327D
题目大意:大概就是说找一个最小的
k
k
k,对序列
p
p
p进行转换之后,存在一个起始位置
i
i
i使得
c
[
i
]
=
c
[
p
[
i
]
]
=
c
[
p
[
p
[
i
]
]
]
…
…
c[i]=c[p[i]]=c[p[p[i]]]……
c[i]=c[p[i]]=c[p[p[i]]]……(一条无限长的路径)。
思路:首先有一个结论就是,对于一个
n
n
n的全排列
p
p
p,我们把它当成一个有
n
n
n个顶点的图,给
(
i
,
p
[
i
]
)
(i,p[i])
(i,p[i])连一条无向边,那么得到的图一定是由几个独立的环组成的(自环也算)。所以依次在每个环上找最小的
k
i
k_i
ki,最后总体取个最小值就是答案了。我们假设某个环有
m
m
m个元素组成,设为
c
y
c
l
e
[
1
…
m
]
cycle[1…m]
cycle[1…m],依据定义有:
p
[
c
y
c
l
e
i
]
=
c
y
c
l
e
i
+
1
m
o
d
m
p[cycle_i]=cycle_{i+1\ mod\ m}
p[cyclei]=cyclei+1 mod m
p
2
[
c
y
c
l
e
i
]
=
p
[
p
[
c
y
c
l
e
i
]
]
=
c
y
c
l
e
i
+
2
m
o
d
m
p^2[cycle_i]=p[p[cycle_i]]=cycle_{i+2}\ mod\ m
p2[cyclei]=p[p[cyclei]]=cyclei+2 mod m
…
…
……
……
p
k
[
c
y
c
l
e
i
]
=
c
y
c
l
e
i
+
k
m
o
d
m
p^k[cycle_i]=cycle_{i+k}\ mod\ m
pk[cyclei]=cyclei+k mod m所以对每个环来说,我们就是要在这个环上找到一条步长最小的无限长的路径。
那么对于步长
k
k
k来说,循环节的长度就是
m
/
g
c
d
(
k
,
m
)
m/gcd(k,m)
m/gcd(k,m),那么对于
g
c
d
(
k
1
,
m
)
=
g
c
d
(
k
2
,
m
)
gcd(k_1,m)=gcd(k_2,m)
gcd(k1,m)=gcd(k2,m)的情况来说,其实只需要验证其中
1
1
1个就行了,后者和前者仅仅是顺序不同而已。所以验证可以整除
m
m
m的步长就行了。也就是
m
m
m的因子。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2e5+5;
int t,n,len,ans;
int p[maxn],c[maxn],cycle[maxn],vis[maxn];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&p[i]);
for(int i=1;i<=n;i++)
scanf("%d",&c[i]);
ans=INF;
for(int pos=1;pos<=n;pos++)//枚举开始位置
{
if(vis[pos])
continue;
len=0;
int v=pos;
while(!vis[v]) //找环
{
cycle[++len]=v;
vis[v]=1;
v=p[v];
}
bool flag=1;
for(int i=1;i<=len;i++)
{
if(len%i)
continue; //必须整除
for(int j=1;j<=i;j++)//步长为i 起始位置为j
{
flag=1;
for(int k=j;k+i<=len;k+=i)
{
if(c[cycle[k]]!=c[cycle[k+i]])
{
flag=0;
break;
}
}
if(flag)
{
ans=min(ans,i);
break;
}
}
if(flag)
break;
}
}
printf("%d\n",ans);
memset(vis,0,sizeof(int)*(n+1));
}
return 0;
}