AGC035F Two Histograms

首先,所有不同的操作方式是很好求的。但是不同操作序列并不正好对应一种不同的网格。

考虑什么情况下两个操作序列不同但是求出了不同的网格。如果出现了这种情况:

1
2
3
4
0 0 1 0
0 0 1 0
1 1 1 0
0 0 0 0

那么明显,要么第三行 +3 ,第三列 +2 或者 第三行 +2 第三列 +3 ,会出现两种不同的状况。

除开这种情况,不会有出现两种不同操作序列对应同一个网格图了。神仙zjk已经教了我一个绝妙的证明,可惜我懒得写下来了(逃

我们将一个导致操作序列出现不同的位置称为一个拐角,那么很明显,同一列或同一行不会出现超过一个拐角。因为在 $(i,j)$ 出现拐角,也就是这里导致了 $i$ 行,$j$ 列有两种不同的操作方式,那么 $(i+1,j)$ 和 $(i,j+1)$ 必然都是 $0$ (否则并不能对应两个不同的操作方式)。

设 $f_i$ 为钦定了 $i$ 个拐角的网格图的数量。 $f_i$ 是很好求的,选择 $i$ 行 $i$ 列后进行排列一下即可

设 $f’_i$ 为钦定了 $i$ 个拐角的操作序列的数量,于是我们知道

然后设 $g_i$ 是正好 $i$ 个拐角的操作序列的数量。可以二项式反演一下有

我们要求的是

推式子:

最后是一个二项式定理。

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
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAXN 500006
#define P 998244353
int n , m;

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

int J[MAXN] , iJ[MAXN];

int C( int a , int b ) {
return J[a] * 1ll * iJ[b] % P * iJ[a - b] % P;
}

int main() {
J[0] = iJ[0] = 1;
for( int i = 1 ; i < MAXN ; ++ i ) J[i] = J[i - 1] * 1ll * i % P , iJ[i] = Pow( J[i] , P - 2 );
cin >> n >> m;
int lim = min( n , m );
int c = 0 , res = 0;
for( int i = 0 , zjk ; i <= lim ; ++ i ) {
zjk = C( n , i ) * 1ll * C( m , i ) % P * J[i] % P * Pow( n + 1 , m - i ) % P * Pow( m + 1 , n - i ) % P;
if( c ) zjk = P - zjk;
c ^= 1;
res = ( res + zjk ) % P;
}
cout << res << endl;
}
文章作者: yijan
文章链接: https://yijan.co/agc035f-two-histograms/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog