洛谷 P13280 「CZOI-R4」午夜巡游
洛谷 P13280 「CZOI-R4」午夜巡游——题解
思路
考虑暴力,算了还是不考虑了。
这是一个置换环的模型(我也是第一次见)。
问题转化:
- 将一个排列 $ P $ 看作是一个有向图:每个点 $ i $ 像 $ P_{i} $ 连边,可以形成若干个独立的环。
- 从 $ k $ 点出发巡游,相当于从 $ k $ 点开始在环上移动。
这里需要分成两类进行讨论:
- 经过 $ m $ 次巡游后回到 $ k $ 点(此时,环长 $ L $ 是 $ m $ 的因子);
- 经过 $ m $ 次巡游后并未回到 $ k $ 点。
1. 回到k点
由于排列数非常多,我们不可能一个个单独考虑,于是可以想到用组合数学进行计数。
具体步骤:
-
枚举环长:满足回到 $ k $ 点的环必然满足环长 $ L|m $ 且 $ L \leq n $ ;
-
计算单个环长的方案数:
- $ k $ 点必定在环上,所以构造一个长度为 $ L $ 的环的方案数为 $ C_{n-1}^{L-1} $ ;
- 环内排列方式: $ (L-1)! $ ;
- 环外任意排列方式: $ (n-L)! $ 。(因为我们是从环长的角度计算贡献,所以环外的排列顺序也会产生贡献)
-
综合:
$ 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 $ 的情况。
算法流程:
- 预处理出阶乘信息;
- 对 $ 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\)

浙公网安备 33010602011771号