题解:P13280 「CZOI-R4」午夜巡游

题意

对于 \(n\) 个元素的任意排列 \(p\),给定 \(k\)\(m\),初始变量 \(x=k\),定义 \(f(k,m)\) 表示经过 \(m\)\(x \gets p_x\) 变换后 \(x\) 的值,求所有 \(p\) 排列下的 \(x\) 的和对 \(998244353\) 取模后的结果,即求如下式子:

\[\sum_{p}f(k,m) \pmod{998244353} \]

题解

对于任意排列,都可以由若干不相交的环构成,\(x\) 最初也位于一个环上,经过若干次变换后,我们分情况讨论,分别是 \(x\) 位于原位置 \(k\) 上,\(x\) 位于环上其他位置

第一种情况

\(x\) 经过若干次变换后仍位于 \(k\) 的位置上,令环长为 \(c\),显然 \(c|m\),对于每一种 \(c\) ,计算他可能出现的排列数量,由于 \(k\) 的值已经确定,我么们需要在剩余 \(n-1\) 个选 \(c-1\) 个元素构成环,有 \(\binom{n-1}{c-1}\) 种方案,由于这 \(c\) 个元素构成了一个环,对这 \(c\) 个元素构造圆排列,注意到任意一个圆排列都对应原来的线排列当中的一种排列情况,不同圆排列对应不同情况且总能合法,所以计算这 \(c\) 个元素的圆排列,为 \(Q_c^c=\left ( c-1 \right ) !\),表示该环的排列方案数,的接着考虑剩下 \(n-c\) 个元素的排列方案数,显然为 \(\left ( n-c \right ) !\) 种方案,令这样的 \(c\)\(d\) 个,即区间 \([1,n]\) 当中 \(m\) 的约数的个数,因为只有当 \(c\) 整除 \(m\) 时,才会时变换后回到原位置,出现第一种,第一种情况的总贡献是:

\[k \cdot d \cdot \binom{n-1}{c-1} \cdot \left ( c-1 \right ) ! \cdot(n-c)!=k \cdot d \cdot \left ( n-1 \right ) ! \]

第二种情况

\(x\) 经过若干次变换后位于环的其他位置,环长为 \(c\),显然 \(c \nmid m\),这样的 \(c\) 显然有 \(n-d\) 个,与第一种情况的分析方法类似,我们先钦定变换后的 \(x\),则要从剩余 \(n-2\) 个元素里选 \(c-2\) 个元素,有 \(\binom{n-2}{c-2}\) 种方案构成这个环,\(x\)\(k\) 的相对位置不变,因为 \(x\) 相对于 \(k\) 的位置是在环中 \(k\) 往后 \(m \mod c\) 个元素,因此可以把 \(x\)\(k\) 当作一个元素在环中进行圆排列计算,则环的排列有 \(Q_{c-1}^{c-1}=\left ( c-2 \right ) !\) 种方案。剩余 \(n-2\) 个元素有 \(\left ( n-2 \right ) !\) 个排列方案,于是第二种情况的总贡献是:

\[\left ( n-d \right ) \sum_{i=1,i \neq k}^{n}i \cdot \binom{n-2}{c-2} \cdot \left ( c-2 \right ) ! \cdot \left ( n-2 \right ) !=\left ( \frac{n(n+1)}{2}-k \right )\left ( n-d \right ) \left ( n-2\right )! \]

总结

将两种情况算得的答案相加即可,如下:

\[Ans=\left ( n-d \right ) \left ( \frac{n(n+1)}{2}-k \right )\left ( n-2\right )!+d \cdot k \cdot \left ( n-1 \right ) ! \]

分析一下时间复杂度,先预处理所有数的阶乘,在分解 \(m\) 的因数,时间复杂度为 \(O(n+T\sqrt n)\)

CODE

#include<cstdio>
using namespace std;

typedef long long ll;
const int P=998244353;
const int N=1e7;
ll Q,n,m,k,fac[N];

void init(){
    fac[0]=1;
    for (int i=1;i<=N;i++) fac[i]=(fac[i-1]*i)%P;
}

int main(){
    init();
    scanf("%lld",&Q);
    while (Q--){
        scanf("%lld%lld%lld",&n,&m,&k);
        ll d=0;
        if (!m) d=n;
        for (int i=1;i*i<=m&&i<=n;i++){
            if (m%i==0){
                if (i*i==m){
                    d++;
                    continue;
                }
                else if (m/i<=n) d+=2;
                else d++;
            }
        }
        ll ans=0;
        ans=(k*fac[n-1]%P*d)%P+((n*(n+1)/2-k)%P*fac[n-2])%P*(n-d)%P;
        ans%=P;
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2025-07-13 21:01  ZYStream  阅读(86)  评论(1)    收藏  举报