4.11小测
P13280 「CZOI-R4」午夜巡游
题目描述
有一个长度为 \(n\) 的排列 \(p\) (\(1\sim n\) 恰好在 \(p\) 中各出现一次)和一个变量 \(x\),初始时 \(x\) 为 \(k\)。
接下来你需要进行 \(m\) 次巡游,每次巡游会让 \(x\) 变为 \(p_x\)。
求所有可能的 \(p\) 进行 \(m\) 次巡游后 \(x\) 的和,对 \(998244353\) 取模。
输入格式
本题有多组测试数据。
第一行输入 \(1\) 个整数 \(T\)。
接下来 \(T\) 行,每行输入 \(3\) 个整数 \(n,m,k\)。
输出格式
共 \(T\) 行,每行输出 \(1\) 个整数,表示该组数据的答案。
输入输出样例 #1
输入 #1
6
3 5 3
114514 0 100000
501 1 249
9982443 231406890 1
9876543 735134400 421704
10000000 180957102 998140
输出 #1
12
616064221
532050777
653339286
829601668
778347084
说明/提示
【样例解释】
对于第 \(1\) 组测试数据,共有 \(6\) 个可能的 \(p\),下面列举出了所有可能的 \(p\) 和对应的 \(x\) 的变化。冒号前为 \(p\),冒号后为 \(p\) 对应的 \(x\) 的变化。
- \([1,2,3]\):\(3\to3\to3\to3\to3\to3\)。
- \([1,3,2]\):\(3\to2\to3\to2\to3\to2\)。
- \([2,1,3]\):\(3\to3\to3\to3\to3\to3\)。
- \([2,3,1]\):\(3\to1\to2\to3\to1\to2\)。
- \([3,1,2]\):\(3\to2\to1\to3\to2\to1\)。
- \([3,2,1]\):\(3\to1\to3\to1\to3\to1\)。
答案为 \(3+2+3+2+1+1=12\)。
【数据范围】
本题采用捆绑测试。
- Subtask #1(\(15\text{ pts}\)):\(n\le6\),\(m\le10^3\)。
- Subtask #2(\(20\text{ pts}\)):\(m\le1\)。
- Subtask #3(\(20\text{ pts}\)):\(k=1\)。
- Subtask #4(\(20\text{ pts}\)):\(T=1\)。
- Subtask #5(\(25\text{ pts}\)):无特殊限制。
对于 \(100\%\) 的数据,\(1\le T\le10^3\),\(1\le k\le n\le10^7\),\(0\le m\le10^9\)。
题解:
不难发现,由于是全排列,所以除了 \(k\) 之外的数都可以视作是等价的,因此除去 \(k\) 之外的数作为巡游终点的方案数也必定相等。
我们不妨先设终点为 \(k\) 的方案数有 \(t\) 种,
那么最终答案即为:
用高斯求和公式处理一下:
接下来问题就变为如何求 \(a\)。
由于最终的排列一定会形成若干个环,
所以这次巡游的终点若为 \(k\),则 \(k\) 所在的环的大小为 \(m\) 的因数。
分析一下当环为不同长度时的方案数。
设环的长度为 \(s\),这个环上第 \(i\) 个点 为 \(c_i\),当前排列为 \(p\)。
不难发现,如果这个环成立,则满足对于所有 \(1 \leq i \leq s\),\(p_{c_i}=c_{i+1}\)。
则除去最后一个点的 \(p\) 为 \(k\) 之外,其他点都为一个不为 \(k\) 的数。
所以概率为 \(\frac{n-1}{n} \cdot \frac{n-2}{n-1} \cdot \frac{n-3}{n-2} \cdots \frac{n-s-2}{n-s-3} \cdot \frac{1}{n-s-1}=\frac{1}{n}\)
又因为总方案数为 \(n!\)
因此对于任意的 \(s\),都有排列个数为 \(n! \times \frac{1}{n}=(n-1)!\)
则 \(t=cnt \times (n-1)!\),其中 \(cnt\) 为小于等于 \(n\) 的 \(m\) 的因数的个数。
所以最终的式子为:
预处理阶乘即可。
Code:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e7 + 7, mod = 998244353;
int Task;
int n, m, k;
ll fac[N];
void init(){
fac[0] = 1;
for(int i = 1; i <= N - 7; i++) fac[i] = fac[i - 1] * i % mod;
}
ll fpow(ll x, ll y){
ll sum = 1;
while(y){
if(y & 1) sum = sum * x % mod;
x = x * x % mod;
y >>= 1;
}
return sum;
}
void Main(){
cin >> n >> m >> k;
if(!m){
printf("%lld\n", fac[n] * k % mod);
return;
}
int cnt = 0;
for(int i = 1; i * i <= m; i++){
if(m % i) continue;
if(i <= n) cnt++;
if(i * i != m && m / i <= n) cnt++;
}
ll t = cnt * fac[n - 1] % mod;
ll up = (fac[n] - t + mod) % mod * (1ll * n * (n + 1) % mod - 2 * k % mod + mod) % mod;
printf("%lld\n", (t * k % mod + up * fpow(2 * n - 2, mod - 2) % mod) % mod);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
init();
cin >> Task;
while(Task--) Main();
return 0;
}
浙公网安备 33010602011771号