[HAOI2018]染色-二項反転

タイトル

  LOJ2527

解析中

  $ S $回に表示される少なくとも$ i $色のスキームの数を$ f_i $とし、$ i $色が$ S $回表示されることを指定します。残りはオプションです。$ f_i = \ binom {m} {i } * \ frac {n!} {(S!)^ i(n-iS)!} *(mi)^ {n-iS} $、そのうち$ \ frac {n!} {(S!)^ i( n-iS)!} $は、$ n $位置で$ n-iS $位置を選択し、$ i $色を入力し、各色の計画数を$ S $回入力することを意味します。

  $ g_i $を設定することは、$ S $の発生の色が$ i $スキームの数と正確に一致することを意味し、$ f_i = \ sum_ {j = i} \ binom {j} {i} g_j $

  したがって、二項反転が利用可能です:$$ \ begin {align *} g_i&= \ sum_ {j = i}(-1)^ {ji} \ binom {j} {i} f_j \\&= \ frac { 1} {i!} \ Sum_ {j = i} \ frac {(-1)^ {ji}} {(ji)!} * J!* F_j \ end {align *} $$

  たたみ込みは

  $ O(M \ log M)$

 コード:

#include <cstdio> 
#include <iostream> 
#include <algorithm> 
#include <cstring>
 using  namespace std; 
typedef long  long ll;
const  int maxn = 200005、mod = 1004535809、g = 3 ; 

inline int read()
{ 
    int ret、f = 1 ;
    char c;
    while((c = getchar())&&(c < ' 0 ' || c> ' 9 '))if(c == '- ')f = -1 ; 
    ret = c- ' 0 ' ;
    while((c = getchar())&&(c> = ' 0 ' && c <= ' 9 '))ret =(ret << 3)+(ret << 1)+ c- ' 0 ' ;
    ret * fを返します。
} 

int add(int x、int y)
{ 
    return x + y <mod?x + y:x + y- mod; 
} 

int rdc(int x、
{
     x-y < 0を返すx-y + mod:x- y; 
} 
ll qpow(ll x、int y)
{ 
    ll ret = 1 ;
    while (y)
    { if(y&1 
            ret = ret * x%mod; 
        x = x * x%mod; 
        y >> = 1 ; 
    } retを返します。
} int n、m、s、lim、bit、rev [maxn << 1 ];
int fac [ 10000005 ]、fnv [ 10000005

        
    

]、a [maxn]; 
ll ginv、f [maxn << 1 ]、h [maxn << 1 ]; 

void init()
{ 
    int t = max(n、m); 
    ginv = qpow(g、mod- 2 ); 
    fac [ 0 ] = 1 ;
    forint i = 1 ; i <= t; ++ i)
        fac [i] = 1LL * fac [i- 1 ] * i%mod; 
    fnv [t] = qpow(fac [t]、mod- 2 );
    forint i = t- 1 ; i> = 0; - I)
        FNV [I] = 1LL * FNV [I + 1 ] *(I + 1)%のMOD。
} 

int comb(int x、int y)
{ 
    if(x <y || y < 0return  0 ;
    return(1LL * fac [x] * fnv [y]%mod)* fnv [xy]%mod; 
} 

void NTT_init(int x)
{ 
    lim = 1 ; 
    ビット = 0 ;
    while(lim <= x)
    { 
        lim<< = 1 ;
        ++ ビット; 
    } 
    forint i = 1 ; i <lim; ++ i)
        rev [i] =(rev [i >> 1 ] >> 1)| ((i&1)<<(ビット-1 )); 
} 

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

int main()
{ 
    n = read(); m = read(); s = read();
    forint i =0 ; i <= m; ++ i)
        a [i] = read();
    int sj = min(m、n / s); 
    初期化(); 
    forint i = 0 ; i <= sj; ++ i)
        f [i] =((((1LL * comb(m、i)* fac [n]%mod)* qpow(fnv [s]、i) %mod)* fnv [ni * s]%mod)* qpow(m-i、n-i * s)%mod;
    forint i = 0 ; i <= sj; ++ i)
    { 
        f [i] = f [i] * fac [i]%mod; 
        h [sj -i] =((i&1)?rdc(0 、fnv [i]):fnv [i]);
    } 
    NTT_init(sj<< 1 ); 
    NTT(f、1 ); 
    NTT(h、1 );
    forint i = 0 ; i <lim; ++ i)
        f [i] = f [i] * h [i]%mod; 
    NTT(F、 - 1 )。
    int ans = 0 ;
    forint i = 0 ; i <= sj; ++ i)
        ans = add(ans、(a [i] * f [i + sj]%mod)* fnv [i]%mod); 
    printf(" %d " 、ans);
    0を返し ます
}
コードを表示

おすすめ

転載: www.cnblogs.com/Joker-Yza/p/12676726.html