2022CCPC Online Contest J - Count Permutation

image

英语不好吃大亏,题意想表达的是对于所有 \(n\) 元排列的 \(w(P)\) 最小,并不是满足 \(p_1 = s, \, p_m = t\) 的排列,因此最小的 \(w(P) = 2\),如果你看假了,这个题分讨没法做。

首先 \(w(P) = 2\) 能在什么条件满足?\(P\) 如果是个模 \(n\) 等差数列那么,我们容易发现 \(S = \{p, \, -q\}\) 满足 \(p + q = n\),而显然公差 \(d\) 满足 \(\gcd(d, n) = 1\),否则生成的模 \(n\) 序列是原剩余系的子群。

而设 \(a_i = id\),那么 \(a_1 = d \equiv s \pmod n, \, a_m = md \equiv t \pmod n\),作差可得

\[(m - 1)d \equiv t - s \pmod n \]

那么不妨写出公式

\[\sum_{d = 1}^{n}\bigg[n \mid (m - 1)d - (t - s)\bigg]\bigg[\gcd(d, n) = 1\bigg] \]

来一发莫比乌斯反演

\[\begin{aligned} &= \sum_{d = 1}^{n - 1}\bigg[n \mid (m - 1)d - (t - s)\bigg]\sum_{k \mid \gcd(d, \, n)}\mu(k) \\ &= \sum_{d = 1}^{n - 1}\bigg[n \mid (m - 1)d - (t - s)\bigg]\sum_{k \mid d, \, k \mid n}\mu(k) \\ &= \sum_{k \mid n}\mu(k)\sum_{p = 1}^{\left\lfloor\frac{n - 1}{k}\right\rfloor}\bigg[n \mid (m - 1)kp - (t - s)\bigg]\end{aligned} \]

为了得到

\[\sum_{p = 1}^{\left\lfloor\frac{n - 1}{k}\right\rfloor}\bigg[n \mid (m - 1)kp - (t - s)\bigg] \]

的值,我们不妨考虑这样一个同余方程,在 \([1, \, N]\) 范围内,\(p\) 有多少解?

\[pm \equiv t \pmod n \]

如果设 \(G = \gcd(n, m)\),那么当 \(G \nmid t\) 时,一定无解,否则一定有解。

考虑有解的情况,设 \(n' = \frac{n}{G}, \, m' = \frac{m}{G}, \, t' = \frac{t}{G}\),此时 \(\gcd(n', \, m') = 1\),利用扩欧可以求得一个逆元 \(m'^{-1}\) 和一个特解 \(p_0\),进而得到

\[p_0 \equiv t'm'^{-1} \pmod{n'} \]

由于所有的解满足 \(p_0 + Tn'(T \in \mathbb{N})\) 的形式,根据 \(1 \le p \le N\) 不难得到,\(1 \le p_0 + Tn' \le N\)

进而有 \(\frac{1 - p_0}{n'} \le T \le \frac{N - p_0}{n'}\)

于是答案可以写作

\[\left\lfloor\frac{N - p_0}{n'}\right\rfloor - \left\lceil\frac{1 - p_0}{n'}\right\rceil \]

为了防止因为“特性”导致除法按照绝对值下取整,我们给分子同时加一个 \(n'\) 就好。

返回原题,把符合条件的系数带入即可。

\(n\) 进行 \(O\left(\sqrt{\frac{n}{\ln{n}}}\right)\) 的质因数分解得,可以计算每个因数的莫比乌斯函数,右边的式子需要解一个同余方程,复杂度 \(O(\log{n})\),上限 \(O\left(d(n)\log{n}\right)\),由于 \(\mu(k)\) 函数的特殊性,有很大一部分都是 \(0\),不用考虑,显然跑不满,每个因数只出现 \(0\)\(1\) 次才是有意义的,两者复杂度上限是负相关的,因此可以通过。

时间复杂度 \(O\left(\sqrt{\frac{n}{\ln{n}}} + d(n)\log{n}\right)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
ll n, m, s, t, ans, res;
ll primes[N], cnt, factor[N], sz;
bool st[N];

void init() {
    for (int i = 2; i <= N; i ++ ) {
        if (!st[N]) primes[cnt ++ ] = i;
        for (int j = 0; i * primes[j] < N; j ++ ) {
            st[i * primes[j]] = 1;
            if (i % primes[j] == 0) break;
        }
    }
}

ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) return x = 1, y = 0, a;
    ll d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

void dfs(ll sum, ll mu, ll p) {
    if (p == sz) {
        // for (int i = 1; i <= (n - 1) / sum; i ++ ) {
        //     if (((m - 1) * sum * i - (t - s)) % n == 0) {
        //         res += mu;
        //     }
        // }
        ll G = __gcd(n, (m - 1) * sum);
        if (abs(s - t) % G != 0) return;
        ll coff = (s - t) / G, _m = (m - 1) * sum / G, _n = n / G;
        ll a, b, sol;
        exgcd(_m, _n, a, b);
        sol = coff * a % _n;
        ans += mu * (((n - 1) / sum - sol) / _n - (_n - sol) / _n + 1);
        // cout << sum << " " << mu << " " << ans << " " << res << "\n";
        return;
    }
    dfs(sum, mu, p + 1);
    dfs(sum * factor[p], -mu, p + 1);
}

int main() {
    init();
    cin >> n >> m >> s >> t;
    ll x = n;
    for (int i = 0; i < cnt && x >= primes[i]; i ++ ) {
        if (x % primes[i] == 0) {
            factor[sz ++ ] = primes[i];
            while (x % primes[i] == 0) x /= primes[i];
        }
    }
    if (x > 1) factor[sz ++ ] = x;
    dfs(1, 1, 0);
    cout << ans << "\n";
    return 0;
}
posted @ 2025-03-27 15:49  YipChip  阅读(13)  评论(0)    收藏  举报