由于存在一道题叫做白兔之舞就导致这道题看上去非常的板(......()如果按照这个方法推下来
约定,([x])表示(x)向下取整
然后会想到可以将([dfrac{i}{k}])拆开变成:
于是原问题变成:
看到了(i\%k),然后还有组合数,然后(k)还是(2)的整数次幂,这个时候当然是单位根反演上场了...
然后就是喜闻乐见的化式子了
然后肯定要交换求和顺序变成:
然后这里是可以将其拆开的...
我们先忽略(dfrac{1}{k^2})
第二个式子很好处理,我们直接用二项式定理即可化开变成:
然后对于某一个(j),((omega_{k}^{j}p+1)^n)为定值,设其为(a_j)
第一个式子可以将组合数拆开
然后这个时候可以将(i=0)的那一项给忽略掉
后面那一坨对于某一个(j)也是固定的,我们可以设(b_j=omega_{k}^jp*(omega_{k}^jp+1)^{n-1})
那么就有原式即:
然后我们就得到了一个(O(k^2))的做法了...
然后我们用一下白兔之舞的神仙操作(tj=dfrac{(t+j)(t+j-1)}{2}-dfrac{j(j-1)}{2}-dfrac{t(t-1)}{2})
证明也很简单,分子是:
所以原式就可以化成:
下面的组合数都反了,但我懒得改了QAQ
然后如果我们将(t+k)看作一个整体那么就会发现这是一个卷积的形式
然后就只需要将(omega_{k}^{inom{2}{t}})和(omega_k^{inom{2}{j}} imes b_j)以及(t imes omega_{k}^{inom{2}{t}})和(omega_k^{inom{2}{j}} imes a_j)分别卷起来即可,暴力卷是需要( m 6NTT)的...
还担心会( m T)来着...然而事实上也不慢,开( m O2)最慢的点才(1.3s)..不过其实可以做( m 3FFT)的...因为卷起来可以将结果乘起来再加起来...同时把两个多项式的( m DFT)算出来需要(3)次变(2)次的技巧...所以速度应该会还可以
复杂度(O(klog k))
(Code:)
#include<bits/stdc++.h>
using namespace std ;
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define re register
#define LL long long
#define int long long
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int Gi = 332748118 ;
const int P = 998244353 ;
const int N = 4e6 + 5 ;
const int G = 3 ;
int n, p, m, limit, L, R[N] ;
LL A[N], B[N], F[N], F2[N], Dg[N], Inv ;
LL fpow( LL x, int k ) {
LL ans = 1, base = x % P ; k = k % ( P - 1 ) ;
while( k ) {
if( k & 1 ) ans = ( ans * base ) % P ;
base = ( base * base ) % P, k >>= 1 ;
}
return ans % P ;
}
void NTT( LL *a, int type ) {
for( re int i = 0; i < limit; ++ i ) if( i < R[i] ) swap( a[i], a[R[i]] ) ;
for( re int k = 1; k < limit; k <<= 1 ) {
LL dg = fpow( ( type == 1 ) ? G : Gi, ( P - 1 ) / ( k << 1 ) );
for( re int i = 0; i < limit; i += ( k << 1 ) )
for( re LL j = i, g = 1; j < i + k; ++ j, g = ( g * dg ) % P ) {
LL Nx = a[j], Ny = ( a[j + k] * g ) % P;
a[j] = ( Nx + Ny ) % P, a[j + k] = ( Nx - Ny + P ) % P ;
}
}
if( type != 1 ) rep( i, 0, limit - 1 ) a[i] = ( a[i] * Inv ) % P ;
}
void Init() {
limit = 1, L = 0 ;
while( limit < m + m ) limit <<= 1, ++ L ;
for( int i = 0; i < limit; ++ i ) R[i] = ( R[i >> 1] >> 1 ) | ( ( i & 1 ) << ( L - 1 ) ) ;
Inv = fpow( limit, P - 2 ) ;
}
LL C( int x ) {
return 1ll * x * ( x - 1 ) / 2 ;
}
signed main()
{
n = gi(), p = gi(), m = gi() ;
Dg[0] = 1, Dg[1] = fpow( G, ( P - 1 ) / m ) ;
rep( i, 2, m ) Dg[i] = ( Dg[i - 1] * Dg[1] ) % P ;
rep( i, 0, m - 1 ) A[i] = fpow( Dg[i] * p + 1, n ), B[i] = Dg[i] * p % P * fpow( Dg[i] * p + 1, n - 1 ) % P ;
LL Ans1 = 0, Ans2 = 0, Ans = 0 ;
rep( i, 0, m - 1 ) A[i] = Dg[C(i) % m] * A[i] % P, B[i] = Dg[C(i) % m] * B[i] % P,
F[i] = Dg[C(i) % m] % P, F2[i] = 1ll * i * Dg[C(i) % m] % P ;
Init(), NTT( A, 1 ), NTT( B, 1 ), NTT( F, 1 ), NTT( F2, 1 ) ;
rep( i, 0, limit - 1 ) A[i] = ( F2[i] * A[i] ) % P, B[i] = ( F[i] * B[i] ) % P ;
NTT( A, -1 ), NTT( B, -1 ) ;
rep( i, 0, limit ) A[i] = A[i] * Dg[m - C(i) % m] % P, B[i] = B[i] * Dg[m - C(i) % m] % P,
Ans2 = ( Ans2 + A[i] ) % P, Ans1 = ( Ans1 + B[i] ) % P ;
Ans1 = ( 1ll * Ans1 * ( n % P ) ) % P ;
Ans = ( Ans1 - Ans2 + P ) % P ;
Ans = ( Ans * fpow( m * m % P, P - 2 ) ) % P ;
printf("%lld
", Ans ) ;
return 0 ;
}
因为多项式实在是做少了所以调得非常自闭...,下次再写我开数组绝对不这么吝啬QAQ我错了