5813. 【NOIP提高A组模拟2018.8.14】 计算(结论 + DP 或 容斥)

5813. 【NOIP提高A组模拟2018.8.14】 计算

Problem

这里写图片描述

  • n 10 9 , m 100
Solution
  • 正难则反.

  • 考虑所有 x i 的可能取值,那么最终组成的长度为 2 m 的序列,总共有

    d ( n ) 2 m

  • 其中 d ( n ) 表示 n 的约数个数.

  • 那么考虑如果满足

    i = 1 2 m x i < n m

  • 然后把所有 x i 变为 n x i ,即可以得到

    i = 1 2 m n x i > n m

  • s 1 表示满足

    i = 1 2 m x i < n m
    的方案数.

  • s 2 表示满足

    i = 1 2 m n x i > n m
    的方案数.

  • 可以得到 s 1 = s 2 .

  • 如果现在可以算出

    i = 1 2 m x i = n m
    .

  • 则可以很容易的计算最终答案的值了.

  • 这个式子可以考虑把 n 分解质因数,那么对于每一个质数,其是互相独立的.

  • 于是问题可以转化为你要构造一个长为 2 m 的序列 x ,满足

    i [ 1 , 2 m ] , 0 x i q
    x i = m q
    .

  • 然后这个显然可以用一个 O ( n m ) d p 去做,其中 n 表示序列长度, m 表示每个位置最多能放多少个数。

  • 但实际上,这样做太慢了,我们可以考虑容斥.

  • 首先如果每个数的取值都是 0 m q ,且最终总和为 m q ,那么这是一个简单的挡板问题,方案数为

    ( n + m 1 m 1 )

  • g [ i ] 表示至少有 i 个数大于 q 的方案数.

  • 然后考虑在总数 m q 里减去 i ( q + 1 ) ,即把左边每个大于 q 的数的取值范围更新一下,使整个子问题能继续转化为挡板问题.

  • 然后根据二项式反演,可以推得

    i = 0 n ( n i ) ( 1 ) i = [ n = 0 ]

  • 所以容斥系数始终为 0 ,然后就可以解了.

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>

#define M 100000
#define N 2000
#define T 210
#define mo 998244353
#define ll long long
#define F(i, a, b) for (ll i = a; i <= b; i ++)
#define G(i, a, b) for (ll i = a; i >= b; i --)

using namespace std;

ll ans, n, m, tot, sum, d, s2, z[M], bz[M], f[T][T * 30], jc[32 * M + 10], ny[32 * M + 10], g[M];
struct node { ll v, num; } q[N];

ll ksm(ll x, ll y) {
    ll ans = 1;
    for (; y; y >>= 1, x = (x * x) % mo)
        if (y & 1) ans = (ans * x) % mo;
    return ans;
}

ll C(ll n, ll m) {
    return ((jc[n] * ny[m] % mo) * ny[n - m]) % mo;
}

int calc(ll q) {
    ll ans = 0, yes = 1;
    F(i, 0, 2 * m) {
        g[i] = C(m * q - i * (q + 1) + 2 * m - 1, 2 * m - 1) * C(2 * m, i) % mo;
        ans = (ans + yes * g[i]) % mo, yes = - yes;
    }
    return ans;
}

int main() {
    freopen("count.in", "r", stdin);
    freopen("count.out", "w", stdout);

    scanf("%d %d", &n, &m);
    jc[0] = ny[0] = 1;
    F(i, 1, 32 * m) jc[i] = (jc[i - 1] * i) % mo; ny[32 * m] = ksm(jc[32 * m], mo - 2);
    G(i, 32 * m - 1, 1) ny[i] = (ny[i + 1] * (i + 1)) % mo;

    d = 2; int up = int(sqrt(n)) + 1;
    F(i, 2, up) {
        if (!bz[i]) z[++ z[0]] = i;
        F(                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           j, 1, z[0]) {
            if (z[j] * i > up) break;
            bz[i * z[j]] = 1;
            if (i % z[j] == 0) break;
        }
        if (n % i == 0)
            d += 2 - (i * i == n);
    }
    sum = ans = 1;  
    for (int i = 1; i <= z[0] && z[i] * z[i] <= n; i ++) {
        int tot = 0;
        while (n % z[i] == 0) tot ++, n /= z[i];
        sum = (sum * (tot + 1)) % mo;
        if (tot)
            ans = (ans * calc(tot)) % mo;
    }
    if (n > 1)
        sum = (sum * 2) % mo, ans = (ans * calc(1)) % mo;
    sum = ksm(sum, 2 * m);
    printf("%d\n", (((sum + ans) * ksm(2, mo - 2)) % mo + mo) % mo);
}
  • 事实上,我们会发现,这里calc计算是 O ( m l o g n ) 级别的,所以 n 可以开到 10 18 ,运用 M i l l i e r R a b i n O ( n 1 4 ) 分解质因数.

  • 然后 m 可以开大到 10 5 级别.

猜你喜欢

转载自blog.csdn.net/Algor_pro_king_John/article/details/81674385