[CTS2019] Función generador de perlas + inversión binomial

Titulo

  LOJ3120

Analizando

  Considere cómo juzgar si una secuencia satisface la condición. Deje que $ c_i $ represente el número de ocurrencias del número $ i $, luego: $$ \ sum_ {i = 1} ^ D \ left \ lfloor \ frac {c_i} {2} \ right \ rfloor \ geqslant m \\ \ sum_ {i = 1} ^ D \ frac {c_i- (c_i \% 2)} {2} \ geqslant m \\ n- \ sum_ {i = 1} ^ D (c_i \% 2) \ geqslant 2m \\ \ sum_ {i = 1} ^ D (c_i \% 2) \ leqslant n-2m $$

  Cuando $ n-2m <0 $, salida $ 0 $; cuando $ n-2m \ geqslant D $, salida $ D ^ n $

  Supongamos que $ g_i $ significa que exactamente el número de ocurrencias de $ i $ es impar, y $ f_i $ significa que al menos las ocurrencias de $ i $ son impares, y $ f_i = \ sum_ {j = i} \ binom {j} { i} g_j $

  Si encontramos $ f $ entonces podemos encontrar $ g $ por inversión binomial, por lo que ahora necesitamos encontrar una manera de encontrar $ f $

  Considere generar funciones, deje $ A (x) = \ sum_ {i = 0} ^ {\ infty} [i \% 2 == 1] x ^ i = \ frac {e ^ xe ^ {-x}} {2 } $, $ B (x) = \ sum_ {i = 0} ^ {\ infty} x ^ i = e ^ x $

  钦 定 有 $ i $ 个数 出现 奇 数次 , 剩下 的 数 出现 任意 次数 , 则 有 : $$ \ begin {align *} f_i & = \ binom {D} {i} n! [X ^ n] A ^ iB ^ {Di} \\ & = \ binom {D} {i} n! [X ^ n] (\ frac {e ^ xe ^ {- x}} {2}) ^ ie ^ {(Di) x} \ \ & = \ frac {D!} {i! (Di)!} n! * \ frac {1} {2 ^ i} [x ^ n] \ sum_ {j = 0} ^ i \ binom {i} { j} (- 1) ^ {ij} e ^ {jx} e ^ {- (ij) x} e ^ {(Di) x} \\ & = \ frac {D! n!} {i! (Di) ! 2 ^ i} * [x ^ n] \ sum_ {j = 0} ^ i \ binom {i} {j} (- 1) ^ {ij} e ^ {(D-2i + 2j) x} \\ & = \ frac {D! n!} {i! (Di)! 2 ^ i} \ sum_ {j = 0} ^ i \ frac {i!} {j! (ij)!} * (- 1) ^ {ij} * \ frac {(D-2i + 2j) ^ n} {n!} \\ & = \ frac {D!} {(Di)! 2 ^ i} \ sum_ {j = 0} ^ i \ frac {(- 1) ^ {ij} (D-2i + 2j) ^ n} {(ij)!} * \ frac {1} {j!} \ end {align *} $$

  Convolucionar para encontrar $ f $

  然后 二项式 反演 : $$ \ begin {align *} g_i & = \ sum_ {j = i} ^ D (-1) ^ {ji} \ binom {j} {i} f_j \\ & = \ frac { 1} {i!} \ Sum_ {j = i} ^ D \ frac {(- 1) ^ {ji}} {(ji)!} * J! * F_j \ end {align *} $$

  La respuesta final es: $ Ans = \ sum_ {i = 0} ^ {n-2m} g_i $$

  $ O (D \ log D) $

 Código:

#include <cstdio> 
#include <iostream> 
#include <algorithm> 
#include <cstring>
 usando el  espacio de nombres std;
typedef largo  largo ll;
const  int maxn = 200005 , mod = 998244353 , g = 3 ;

int add ( int x, int y)
{
    volver x + y <mod? x + y: x + y - mod;
}

int rdc ( int x, int y)
{
    return x - y < 0? x - y + mod: x - y;
}

ll qpow (ll x, int y)
{
    ll ret = 1 ;
    mientras (y)
    {
        si (y & 1 )
            ret = ret * x% mod;
        x = x * x% mod;
        y >> = 1 ;
    }
    volver a la derecha;
}

int D, n, m;
int lim, bit, rev [maxn << 1 ];
ll fac [maxn], fnv [maxn];
ll ginv, f [maxn << 1 ], A [maxn << 1 ], B [maxn << 1 ];

void init ()
{
    ginv = qpow (g, mod - 2 );
    fac [ 0 ] = 1 ;
    para ( int i = 1 ; i <= D; ++ i)
        fac [i] = fac [i- 1 ] * i% mod;
    fnv [D] = qpow (fac [D], mod - 2 );
    para ( int i = D - 1 ; i> = 0 ; - i)
        fnv [i] = fnv [i + 1 ] * (i + 1 )% mod;
}

nulo NTT_init ( int x)
{
    lim = 1 ;
    bit = 0 ;
    mientras que (lim <= x)
    {
        lim << = 1 ;
        ++ bit;
    }
    para ( int i = 1 ; i <lim; ++ i)
        rev [i] = (rev [i >> 1 ] >> 1 ) | ((i & 1 ) << (bit - 1 ));
}

NTT nulo (ll * x, int y)
{
    para ( int i = 1 ; i <lim; ++ i)
         if (i < rev [i])
            swap (x [i], x [rev [i]]);
    ll wn, w, u, v;
    para ( int i = 1 ; i <lim; i << = 1 )
    {
        wn = qpow ((y == 1 )? g: ginv, (mod - 1 ) / (i << 1 ));
        para ( int j = 0 ; j <lim; j + = (i << 1 ))
        {
            w = 1 ;
            para ( int k = 0 ; k <i; ++ k)
            {
                u = x [j + k];
                v = x [j + k + i] * w% mod;
                x [j + k] = sumar (u, v);
                x [j + k + i] = rdc (u, v);
                w = w * wn% mod;
            }
        }
    }
    si (y == - 1 )
    {
        ll linv = qpow (lim, mod - 2 );
        para ( int i = 0 ; i <lim; ++ i)
            x [i] = x [i] * linv% mod;
    }
}

int main ()
{
    scanf ( " % d% d% d " , & D, & n, & m);
    si (n - 2 * m < 0 )
    {
        printf ( " 0 " );
        devuelve  0 ;
    }
    si (n - 2 * m> = D)
    {
        printf ( " % lld " , qpow (D, n));
        devuelve  0 ;
    }
    en eso();
    para ( int i = 0 ; i <= D; ++ i)
    {
        A [i] = qpow (rdc (D, 2 * i), n) * fnv [i]% mod;
        A [i] = ((i & 1 )? Rdc ( 0 , A [i]): A [i]);
        B [i] = fnv [i];
    }
    NTT_init (D << 1 );
    NTT (A, 1 );
    NTT (B, 1 );
    para ( int i = 0 ; i <lim; ++ i)
        A [i] = A [i] * B [i]% mod;
    NTT (A, - 1 );
    para ( int i = 0 ; i <= D; ++ i)
        A [i] = ((A [i] * fac [D]% mod) * qpow (qpow ( 2 , i), mod - 2 )% mod) * fnv [Di]% mod;
    para ( int i = D + 1 ; i <lim; ++ i)
        A [i] = 0 ;
    para ( int i = 0 ; i <= D; ++ i)
    {
        A [i] = A [i] * fac [i]% mod;
        f [D -i] = ((i & 1 )? rdc ( 0 , fnv [i]): fnv [i]);
    }
    NTT (A, 1 );
    NTT (f, 1 );
    para ( int i = 0 ; i <lim; ++ i)
        f [i] = f [i] * A [i]% mod;
    NTT (f, - 1 );
    int ans = 0 ;
    para ( int i = 0 ; i <= n - 2 * m; ++ i)
        ans = add (ans, f [D + i] * fnv [i]% mod);
    printf ( " % d " , ans);
    devuelve  0 ;
}
Ver código

Supongo que te gusta

Origin www.cnblogs.com/Joker-Yza/p/12676806.html
Recomendado
Clasificación