数论 + 高精度(思维题) - Bogo Sort - 2020牛客暑期多校训练营(第五场)

数论 + 高精度(思维题) - Bogo Sort - 2020牛客暑期多校训练营(第五场)

题意:

给 定 一 个 长 度 为 n 的 置 换 ( p 1 , p 2 , . . . , p n ) , 求 有 多 少 个 排 列 可 通 过 这 个 置 换 变 成 顺 序 。 给定一个长度为n的置换(p_1,p_2,...,p_n),求有多少个排列可通过这个置换变成顺序。 n(p1,p2,...,pn)

示例1
输入

5
1 2 3 4 5

输出

1

示例2
输入

6
2 3 4 5 6 1

输出

6

数据范围:

1 ≤ N ≤ 1 0 5 1≤N≤10^5 1N105

注: 答 案 对 1 0 N 取 模 。 答案对10^N取模。 10N


分析:

题 意 即 , 每 次 将 序 列 中 的 第 p i 项 移 动 到 第 i 项 , 求 多 少 个 序 列 能 够 在 给 定 置 换 序 列 p i 的 变 换 下 有 序 。 题意即,每次将序列中的第p_i项移动到第i项,求多少个序列能够在给定置换序列p_i的变换下有序。 piipi

首 先 可 以 确 定 的 是 , 任 意 给 定 的 置 换 , 至 少 有 一 解 ( 有 序 序 列 就 是 一 组 解 ) 。 首先可以确定的是,任意给定的置换,至少有一解(有序序列就是一组解)。 ()

接 着 , 如 何 计 算 解 的 数 量 ? 接着,如何计算解的数量?

在这里插入图片描述
我 们 不 妨 直 接 从 有 序 序 列 进 入 变 换 , 我们不妨直接从有序序列进入变换,

我 们 在 p i 与 i 之 间 连 接 一 条 边 , 我们在p_i与i之间连接一条边, pii

那 么 每 进 行 一 次 置 换 , 环 上 的 数 字 就 会 依 次 旋 转 一 个 位 置 , 那么每进行一次置换,环上的数字就会依次旋转一个位置,

当 每 个 环 上 的 数 字 旋 转 一 周 时 , 环 上 的 数 字 就 回 到 了 初 始 有 序 的 状 态 , 当每个环上的数字旋转一周时,环上的数字就回到了初始有序的状态,

而 当 整 个 序 列 所 有 环 上 的 数 都 在 同 一 时 刻 回 到 初 始 有 序 状 态 时 , 整 个 序 列 再 次 回 到 有 序 状 态 , 而当整个序列所有环上的数都在同一时刻回到初始有序状态时,整个序列再次回到有序状态,

那 么 所 有 的 中 间 状 态 , 就 是 满 足 条 件 的 所 有 序 列 , 那么所有的中间状态,就是满足条件的所有序列,

这 些 序 列 的 数 量 应 当 为 所 有 环 的 周 期 的 最 小 公 倍 数 。 这些序列的数量应当为所有环的周期的最小公倍数。

因 此 , 本 题 的 任 务 即 求 所 有 环 的 长 度 , 再 对 它 们 求 最 小 公 倍 数 。 因此,本题的任务即求所有环的长度,再对它们求最小公倍数。

由 于 答 案 可 能 较 大 , 我 们 要 计 算 高 精 度 。 由于答案可能较大,我们要计算高精度。

L C M ( a , b ) = a × b G C D ( a , b ) , 计 算 最 大 公 约 数 时 , 可 以 做 如 下 转 化 : LCM(a,b)=\frac{a×b}{GCD(a,b)},计算最大公约数时,可以做如下转化: LCM(a,b)=GCD(a,b)a×b:

要 计 算 第 i 个 环 与 前 i 个 环 的 最 小 公 倍 数 , 设 前 i − 1 个 环 的 最 小 公 倍 数 为 C , 第 i 个 环 的 周 期 为 c n t i , 要计算第i个环与前i个环的最小公倍数,设前i-1个环的最小公倍数为C,第i个环的周期为cnt_i, iii1Cicnti

计 算 C 和 c n t i 的 最 大 公 约 数 , 根 据 辗 转 相 除 法 , 等 价 于 计 算 C % c n t i 和 c n t i 的 最 大 公 约 数 。 计算C和cnt_i的最大公约数,根据辗转相除法,等价于计算C\%cnt_i和cnt_i的最大公约数。 CcntiC%cnticnti

由 于 C 是 由 数 组 存 储 的 , C = ∑ i = 1 l e n C i × 1 0 i − 1 , 故 C % c n t i = ∑ i = 1 l e n ( C i × 1 0 i − 1 % c n t i ) , 由于C是由数组存储的,C=\sum_{i=1}^{len}C_i×10^{i-1},故C\%cnt_i=\sum_{i=1}^{len}(C_i×10^{i-1}\%cnt_i), CC=i=1lenCi×10i1C%cnti=i=1len(Ci×10i1%cnti),

这 样 , 我 们 就 能 够 将 求 最 大 公 约 数 转 化 到 整 形 范 围 内 的 计 算 。 这样,我们就能够将求最大公约数转化到整形范围内的计算。

代码:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

const int N=1e5+10;

int n,p[N];
int cnt[N],idx;
bool vis[N];
int C[N],len;

int gcd(int a,int b)
{
    return b ? gcd(b,a%b) : a;
}

int gcd(int x)
{
     int t=0;
     for(int i=len;i;i--)
     {
         t=t*10+C[i];
         t%=x;
     }
     return gcd(t,x);
}

void mul(int k)
{
    int n=len+5;
    for(int i=1;i<=n;i++) C[i]*=k;
    for(int i=1;i<=n;i++)
    {
        C[i+1]+=C[i]/10;
        C[i]%=10;
    }
    while(C[n]==0) n--;
    len=n;
}  

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&p[i]);
    
    for(int i=1;i<=n;i++)
        if(!vis[i])
        {   
            idx++;
            int j=i;
            while(!vis[j])
            {
                vis[j]=true;
                cnt[idx]++;
                j=p[j];
            }
        }
        
    len=1,C[1]=1;
    for(int i=1;i<=idx;i++)
    {
        cnt[i]/=gcd(cnt[i]);
        mul(cnt[i]);
    }
    
    for(int i=len;i;i--) printf("%d",C[i]);
    
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值