CTS2019 珍珠

题意:对于任意 $n$ 个 $[1,D]$ 的数,求其中能找出 $m$ 对相同的数的方案数量。

首先,特判掉 $2m \le n - D$ , $2m > n$ 的情况,明显答案分别是 $D^n,0$。

稍微推下式子,不难发现题设条件即为出现次数为奇数的数个数不超过 $n-2m$ 的方案数量。

我们假设钦定 $i$ 个数字出现次数是奇数的方案数量是 $g_i$ ,恰好有 $i$ 个数被出现次数为奇数的方案数量是 $f_i$ ,那么由二项式反演我们有

这明显是个卷积的形式,于是我们最后的答案就是

也就是说,只需要算出 $g$ 做个差卷积即可算出 $f$ ,从而算出答案。

如何求出 $g$ ?

由于需要给数字做排列,需要算 EGF ,而偶数的 EGF 是

我们考虑如何求 $g_i$ ,需要钦定 $i$ 个数是奇数,写成式子就是

剩下的数出现次数任意,也就是 $e^{(D-i)x}$,所以可以写出 $g$ 的式子

为了好看可以改成枚举 $i-j$ ,于是就是

直接卷积即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 1000006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
#define P 998244353
#define lg2( x ) ( 31 - __builtin_clz( x ) )
int n , m; ll q;

int Pow( int x , int a ) {
int ans = 1 , cur = x % P;
while( a ) {
if( a & 1 ) ans = 1ll * ans * cur % P;
cur = 1ll * cur * cur % P , a >>= 1;
}
return ans;
}

int Wn[2][MAXN] , rev[MAXN];
void getwn( int len ) {
for( int mid = 1 ; mid < len ; mid <<= 1 ) {
int w0 = Pow( 3 , ( P - 1 ) / ( mid << 1 ) ) , w1 = Pow( 3 , P - 1 - ( P - 1 ) / ( mid << 1 ) );
Wn[0][mid] = Wn[1][mid] = 1;
for( int i = 1 ; i < mid ; ++ i )
Wn[0][mid + i] = 1ll * Wn[0][mid + i - 1] * w0 % P,
Wn[1][mid + i] = 1ll * Wn[1][mid + i - 1] * w1 % P;
}
}
void getr( int len ) {
int t = __builtin_ctz( len ) - 1;
for( int i = 1 ; i < len ; ++ i ) rev[i] = ( rev[i >> 1] >> 1 ) | ( ( i & 1 ) << t );
}
void NTT( int* A , int len , int typ ) {
rep( i , 0 , len - 1 ) if( i < rev[i] ) swap( A[i] , A[rev[i]] );
for( int mid = 1 ; mid < len ; mid <<= 1 )
for( int i = 0 ; i < len ; i += ( mid << 1 ) )
for( int k = 0 ; k < mid ; ++ k ) {
int t1 = A[i + k] , t2 = 1ll * A[i + k + mid] * Wn[typ][mid + k] % P;
A[i + k] = (t1 + t2) % P , A[i + k + mid] = (t1 + P - t2) % P;
}
if( typ == 1 ) for( int inv = Pow( len , P - 2 ) , i = 0 ; i < len ; ++ i ) A[i] = 1ll * A[i] * inv % P;
}

int J[MAXN] , iJ[MAXN];
int g[MAXN];
int a[MAXN] , b[MAXN] , iv2;
inline int pp( const int& x ) {
return ( x & 1 ) ? P - 1 : 1;
}
inline void mul( int *A , int *B , int n , int m , int bc = 0 ) {
int len = 1;
while( len <= n + m ) len <<= 1;
rep( i , n + 1 , len ) A[i] = 0;
rep( j , m + 1 , len ) B[j] = 0;
getr( len ) , getwn( len );
NTT( A , len , 0 ) , NTT( B , len , 0 );
rep( i , 0 , len - 1 ) A[i] = A[i] * 1ll * B[i] % P;
NTT( A , len , 1 );
if( !bc ) return;
NTT( B , len , 1 );
}
int C( int a , int b ) {
return J[a] * 1ll * iJ[b] % P * iJ[a - b] % P;
}
void getg( int n ) {
rep( i , 0 , n ) a[i] = pp( i ) * 1ll * iJ[i] % P * ( Pow( n - 2 * i , q ) % P + P ) % P , b[i] = iJ[i];
mul( a , b , n , n );
rep( i , 0 , n ) g[i] = a[i] * 1ll * J[i] % P * Pow( iv2 , i ) % P * C( n , i ) % P;
}

int f1[MAXN] , f2[MAXN] , s1[MAXN] , s2[MAXN];

void solve() {
cin >> n >> q >> m;
if( 2 * m <= q - n ) return printf("%d\n",Pow( n , q ) ) , void();
if( 2 * m > q ) return puts("0") , void();
int t = q - 2 * m;
q %= ( P - 1 );
iv2 = ( P + 1 ) / 2;
J[0] = 1;
int mx = n;
rep( i , 1 , mx ) J[i] = J[i - 1] * 1ll * i % P;
iJ[mx] = Pow( J[mx] , P - 2 );
per( i , mx - 1 , 0 ) iJ[i] = iJ[i + 1] * 1ll * ( i + 1 ) % P;

getg( n );
rep( i , 0 , n ) a[i] = pp( n - i ) * 1ll * iJ[n - i] % P , b[i] = J[i] * 1ll * g[i] % P;
int len = 1;
mul( a , b , n , n );
int res = 0;
rep( i , 0 , n ) if( i <= t ) f1[i] = a[i + n] * 1ll * iJ[i] % P , res = ( res + f1[i] ) % P;
cout << res << endl;
}

signed main() {
// int T;cin >> T;while( T-- ) solve();
solve();
}
文章作者: yijan
文章链接: https://yijan.co/cts2019-zhen-zhu/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog