洛谷P13277 「CZOI-R4」改编
不难发现,进行若干次巡游后一定可以使 \(x\) 再次变为 \(k\),证明:
考虑将点 \(x\) 向点 \(p_x\) 连一条有向边,那么构成一张每个点恰好有 \(1\) 条入边和 \(1\) 条出边的图,相当于证明点 \(k\) 一定在一个环中。
由于点数只有 \(n\) 且每个点都有 \(1\) 条出边,所以从每个点出发都一定会走进一个环。又因为每个点只有 \(1\) 条入边,所以除了点 \(k\) 以外,每个走到过的点,都已经有 \(1\) 条入边了,所以这个环一定有 \(1\) 条边连向点 \(k\),即一定包含点 \(k\)。
考虑枚举进行无限次巡游后 \(x\) 的种类个数 \(a\),求出固定 \(a\) 时的最终 \(x\) 的和(记为 \(w\)),相加即为答案。
先考虑求出固定 \(a\) 时 \(p\) 的方案数。显然 \(p_k\) 有 \(n-1\) 个数可以填(因为不能填自己),\(p_{p_k}\) 有 \(n-2\) 个数可以填。以此类推,可以得到有 \((n-1)(n-2)\cdots(n-a+2)(n-a+1)\) 种方案。没有 \(n-a\) 是因为第 \(a\) 次巡游的 \(p_x\) 必须为 \(k\)。然后其余位置我们可以随便填,第一个没填的位置有 \(n-a\) 个数可以填(因为可以填自己),第二个没填的位置有 \(n-a-1\) 个数可以填。以此类推,可以得到有 \((n-a)(n-a-1)\cdots(2)(1)\) 种方案。
将有用的位置的方案数乘以其余位置的方案数,得到 \((n-1)!\),即为满足进行无限次巡游后 \(x\) 的种类个数为 \(a\) 的方案数,这是与 \(a\) 无关的。然后考虑最终 \(x\) 的值,分类讨论:
- 若 \(a\) 为 \(m\) 的因数,最终 \(x\) 显然一定为 \(k\),所以 \(w=k(n-1)!\)。
- 否则,显然 \(x\) 一定不为 \(k\),因为不为 \(k\) 的数本质上是等价的,最终 \(x\) 为 \(r(r\ne k)\) 的方案数一定相等,都为 \(\frac{(n-1)!}{n-1}=(n-2)!\)。所以 \(w=[1+2+\cdots+(n-1)+n-k](n-2)!=[\frac{n(n+1)}{2}-k](n-2)!\)。
于是我们可以在 \(O(Tn)\) 的时间复杂度内求出答案,实际上手玩或打表很容易发现以上式子。
阶乘可以预处理出来,用一个大小为 \(10^7\) 的数组存。注意特判 \(n=1\) 和 \(m=0\) 的情况。这两种情况答案分别为 \(1\) 和 \(kn!\)。
然后我们考虑优化,因为两种情况的答案都与 \(a\) 无关,我们只需要求出 \(1\sim n\) 中 \(m\) 的因数个数 \(p\),那么最终答案即为 \(pk(n-1)!+(n-p)[\frac{n(n+1)}{2}-k](n-2)!\)。直接从 \(1\) 枚举到 \(\lfloor\sqrt{m}\rfloor\) 求 \(p\) 即可。
时间复杂度 \(O(\max n+T\sqrt{m})\)。使用 Pollard-Rho 算法并用 DFS 枚举因子可以做到时间复杂度 \(O(\max n+T(m^{\frac{1}{4}}+d(n))\),但这不是普及组乃至提高组需要掌握的东西。
代码展示:
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
int t,n,m,k,p,s[10000001];
signed main(){
cin.tie(nullptr)->sync_with_stdio(0);
s[0]=1;
for(int i=1;i<=10000000;i++){
s[i]=s[i-1]*i%mod;
}
cin>>t;
while(t--){
p=0;
cin>>n>>m>>k;
if(m==0){
cout<<k*s[n-1]%mod*n%mod<<'\n';
continue;
}
if(n==1){
cout<<"1\n";
continue;
}
for(int i=1;i*i<=m;i++){
if(m%i==0){
if(i<=n){
p++;
}
if(i*i!=m&&m/i<=n){
p++;
}
}
}
cout<<(k*s[n-1]%mod*p%mod+(n*n+n-2*k)/2%mod*s[n-2]%mod*(n-p)%mod)%mod<<'\n';
}
return 0;
}

浙公网安备 33010602011771号