codeforces 1327D Infinite Path 图论+思维

48 篇文章 1 订阅

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[1m],依据定义有: 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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值