洛谷 P13280 「CZOI-R4」午夜巡游

洛谷 P13280 「CZOI-R4」午夜巡游——题解

原题

思路

考虑暴力,算了还是不考虑了。

这是一个置换环的模型(我也是第一次见)。

问题转化:

  • 将一个排列 $ P $ 看作是一个有向图:每个点 $ i $ 像 $ P_{i} $ 连边,可以形成若干个独立的环。
  • 从 $ k $ 点出发巡游,相当于从 $ k $ 点开始在环上移动。

这里需要分成两类进行讨论:

  1. 经过 $ m $ 次巡游后回到 $ k $ 点(此时,环长 $ L $ 是 $ m $ 的因子);
  2. 经过 $ m $ 次巡游后并未回到 $ k $ 点。

1. 回到k点

由于排列数非常多,我们不可能一个个单独考虑,于是可以想到用组合数学进行计数。

具体步骤:

  1. 枚举环长:满足回到 $ k $ 点的环必然满足环长 $ L|m $ 且 $ L \leq n $ ;

  2. 计算单个环长的方案数:

    • $ k $ 点必定在环上,所以构造一个长度为 $ L $ 的环的方案数为 $ C_{n-1}^{L-1} $ ;
    • 环内排列方式: $ (L-1)! $ ;
    • 环外任意排列方式: $ (n-L)! $ 。(因为我们是从环长的角度计算贡献,所以环外的排列顺序也会产生贡献)
  3. 综合:

    $ S_{1}= \displaystyle\sum_{L|m \ \ && \ \ L \leq n } C_{n-1}^{L-1} \times (L-1)! \times (n-L)! $

发现 $ C_{n-1}^{L-1} \times (L-1)! \times (n-L)! = (n-1)! $ ,于是我们可以对式子进行化简:$ \ $

$ S_{1}=(n-1)! \times d $ (其中 $ d $ 是 $ m $ 的因子中 $ \leq n $ 的数的个数)

2. 未回到k点

显然,未回到 $ k $ 点的环长种类一个共有 $ n-d $ 种。

设当前的环长为 $ L $ ,考虑最终到达 $ p(p \neq k) $ ,方案数也就是从剩下的 $ n-2 $ 个数里选出 $ L-2 $ 个数再乘上环内 $ L-2 $ 个数的排列再乘上环外 $ n-L $ 个数的排列,即:$ \ $
$ \displaystyle \sum_{p=1}^{n \ \ && \ \ p \neq k} p \times C_{n-2}^{L-2} \times (L-2)! \times (n-L)! $

又可以发现 $ C_{n-2}^{L-2} \times (L-2)! \times (n-L)! =(n-2)! $ ,与 $ L $ 没有关系,而 $ p $ 可以用等差数列求和,于是大道至简:

$ S_{2}= (n-d) \times (\frac{(n+1) \times n}{2}-k) \times (n-2)! $ 。


结果与流程

最终结果 $ ans=S_{1}+S_{2} $ 。

需要特判一下 $ m=0 $ 和 $ n=1 $ 的情况。

算法流程:

  1. 预处理出阶乘信息;
  2. 对 $ m $ 计算其小于等于 $ n $ 的因子个数,并计算答案。

时间复杂度:$ O(n+T \sqrt m) $ 。


code

#include <bits/stdc++.h> 
#define i8  __int128
#define int long long 
#define fuck inline
#define lb long double 
using namespace std; 
// typedef long long ll; 
const int N=1e7+5,mod=998244353;
const int INF=1e9+7; 
const int inf=LONG_LONG_MAX/4;
// const int mod1=469762049,mod2=998244353,mod3=1004535809;
// const int G=3,Gi=332748118; 
// const int M=mod1*mod2;
fuck 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<<3)+(x<<1)+(c-'0');c=getchar();}
    return x*f;
}
fuck void write(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
int n,m,k;
int fac[N];
fuck void pre()
{
    fac[0]=1;
    for(int i=1;i<=N-5;i++)fac[i]=fac[i-1]*i%mod;
}
fuck void solve()
{
    cin>>n>>m>>k;
    int d=0;
    if(n==1){cout<<1<<endl;return;}
    if(m==0){cout<<k*fac[n]%mod<<endl;return;}
    for(int i=1;i*i<=m;i++)
    {
        if(m%i!=0)continue;
        if(i<=n)d++;
        if(i*i!=m&&(m/i)<=n)d++;
    }
    cout<<(k*fac[n-1]%mod*d%mod+(n*(n+1)/2-k)%mod*(n-d)%mod*fac[n-2]%mod)%mod<<endl;
}
signed main() 
{ 
    pre();
    // ios::sync_with_stdio(false); 
    // cin.tie(0); cout.tie(0); 
    int QwQ=read();
    // int fuckccf=read();
    while(QwQ--)solve(); 
    // solve(); 
    return 0; 
}
//  6666   66666  666666 
// 6    6  6   6      6 
// 6    6  6666      6 
// 6    6  6  6    6 
//  6666   6   6  6666666

完结收工!!!!!

个人主页

看完点赞,养成习惯

\(\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\)

posted @ 2025-08-09 17:45  Nightmares_oi  阅读(46)  评论(0)    收藏  举报