P5437 【XR-2】约定
每一条边被选中的概率: \(\frac{n-1}{\frac{n(n-1)}{2}}=\frac{2}{n}\)
所以答案为:
\[\frac{2}{n} \sum_{i=1}^{n-1} \sum_{j=i+1}^n (i+j)^k
\]
单独考虑后面的和式:
\[f(n)=\sum_{i=1}^{n-1}\sum_{j=i+1}^n(i+j)^k
\]
运用插值的套路:
\[\Delta (f)=f(n+1)-f(n)=\sum_{i=1}^n\sum_{j=i+1}^{n+1}(i+j)^k-\sum_{i=1}^{n-1}\sum_{j=i+1}^n(i+j)^k=\sum_{i=1}^n(i+n+1)^k=\sum_{i=n+2}^{2n+1}i^k
\]
令 \(S(n)=\sum_{i=1}^n i^k\) ,那么有:
\[\Delta(f)=S(2n+1)-S(n+1)
\]
老祖宗告诉我们 \(S\) 是一个 \(k+1\) 次多项式,那么 \(\Delta(f)\) 也是一个 \(k+1\) 次多项式, \(f\) 应该是一个 \(k+2\) 次多项式。
众所周知前 \(k\) 项的自然数幂和可以 \(\mathcal O(k)\) 预处理,那么暴力算出 \(f\) 前 \(k+3\) 项的复杂度为 \(\mathcal O(k)\)。
然后 \(\mathcal O(k)\) 插值即可。
做了这道题才发现自己 \(\mathcal O(k)\) 的插值是假的
#include <cstdio>
#include <iostream>
using namespace std;
#define LL long long
const int MAXN = 20000010 , Mod = 998244353;
int Quick_pow( int x , int po ) {
int Ans = 1;
for( ; po ; po >>= 1 , x = 1ll * x * x % Mod )
if( po & 1 ) Ans = 1ll * Ans * x % Mod;
return Ans;
}
int Inv( int x ) {
return ( Quick_pow( x , Mod - 2 ) + Mod ) % Mod;
}
int Add( int x , int y ) { x += y; return x >= Mod ? x - Mod : x; }
int Sub( int x , int y ) { x -= y; return x < 0 ? x + Mod : x; }
int Mul( int x , int y ) { return 1ll * x * y % Mod; }
int Div( int x , int y ) { return 1ll * x * Inv( y ) % Mod; }
int Fix( int x ) { return ( x % Mod + Mod ) % Mod; }
int fac[ MAXN + 5 ] , ivf[ MAXN + 5 ];
void Init( ) {
fac[ 0 ] = 1;
for( int i = 1 ; i <= min( MAXN , Mod - 1 ) ; i ++ ) fac[ i ] = Mul( fac[ i - 1 ] , i );
ivf[ min( MAXN , Mod - 1 ) ] = Inv( fac[ min( MAXN , Mod - 1 ) ] );
for( int i = min( MAXN , Mod - 1 ) ; i >= 1 ; i -- ) ivf[ i - 1 ] = Mul( ivf[ i ] , i );
}
int binom( int n , int m ) {
if( n < m ) return 0;
return Mul( fac[ n ] , Mul( ivf[ m ] , ivf[ n - m ] ) );
}
int n , k;
int prnum , prime[ MAXN + 5 ] , Sk[ MAXN + 5 ];
bool vis[ MAXN + 5 ];
void sieve( ) {
Sk[ 1 ] = 1;
for( int i = 2 ; i <= MAXN ; i ++ ) {
if( !vis[ i ] ) {
prime[ ++ prnum ] = i;
Sk[ i ] = Quick_pow( i , k );
}
for( int j = 1 ; j <= prnum && 1ll * i * prime[ j ] <= MAXN ; j ++ ) {
vis[ i * prime[ j ] ] = 1;
Sk[ i * prime[ j ] ] = Mul( Sk[ i ] , Sk[ prime[ j ] ] );
if( i % prime[ j ] == 0 ) break;
}
}
for( int i = 1 ; i <= MAXN ; i ++ ) Sk[ i ] = Add( Sk[ i ] , Sk[ i - 1 ] );
}
int m;
int pre[ MAXN + 5 ] , suf[ MAXN + 5 ];
int Lagrange( int *fx , int x0 ) {
pre[ 0 ] = 1;
for( int i = 1 ; i <= m ; i ++ ) pre[ i ] = Mul( pre[ i - 1 ] , Sub( x0 , i ) );
suf[ m + 1 ] = 1;
for( int i = m ; i >= 1 ; i -- ) suf[ i ] = Mul( suf[ i + 1 ] , Sub( x0 , i ) );
int fk = 0;
for( int i = 1 ; i <= m ; i ++ ) fk = Add( fk , Mul( fx[ i ] , Mul( Mul( pre[ i - 1 ] , suf[ i + 1 ] ) , Fix( ( ( m - i ) & 1 ? -1 : 1 ) * Mul( ivf[ i - 1 ] , ivf[ m - i ] ) ) ) ) );
return fk;
}
int y[ MAXN + 5 ];
int main( ) {
Init();
scanf("%d %d",&n,&k); m = k + 3;
sieve();
for( int i = 0 ; i < m ; i ++ )
y[ i + 1 ] = Add( y[ i ] , Sub( Sk[ 2 * i + 1 ] , Sk[ i + 1 ] ) );
printf("%d\n", Mul( Div( 2 , n ) , Lagrange( y , n ) ) );
return 0;
}