「LibreOJ NOI Round #2」不等关系

「LibreOJ NOI Round #2」不等关系

暑假 dls 讲过但是当时掉线了。。

考虑我们如果直接无视 > 符号,现在这个排列就一定是一些下降的子段组合在一起的。我们假设这些段的长度分别是$a_1,a_2,\dots ,a_k$那么算出来方案数量是

因为我们可以任意定一个排列,为了满足条件只需要把这些子段排个序。理解一下发现就是这个东西。

但是我们还有 > 符号啊,既然不好处理,我们可以考虑容斥,比如, <<><> 这个序列,它的答案就可以推出来

1
<<><> = <<*<* - <<<<* - <<*<< + <<<<<

为啥是对的?我们可以把每个位置拖出来,发现 这个位置是 > = 这个位置关系任意 - 这个位置是 >

所以我们可以考虑用 dp 做这个容斥,$dp[i]$表示前$i$个关系被满足的方案数量,则有

这个东西是可以方便的分治FFT的,化开就是:

写的时候不知道哪里系数犯了,算出来是负的。。。

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

namespace cdqntt {
int Pow(int x, int y) {
int res = 1;
while (y) {
if (y & 1) res = res * (ll) x % P;
x = x * (ll) x % P, y >>= 1;
}
return res;
}

int wn[2][MAXN];

void getwn(int l) {
for (int i = 1; i < (1 << l); i <<= 1) {
int w0 = Pow(3, (P - 1) / (i << 1)), w1 = Pow(3, P - 1 - (P - 1) / (i << 1));
wn[0][i] = wn[1][i] = 1;
for (int j = 1; j < i; ++j)
wn[0][i + j] = wn[0][i + j - 1] * (ll) w0 % P,
wn[1][i + j] = wn[1][i + j - 1] * (ll) w1 % P;
}
}

int rev[MAXN];

void getr(int l) { for (int i = 1; i < (1 << l); ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << l - 1); }

void NTT(int *A, int len, int f) {
for (int i = 0; i < len; ++i) if (rev[i] < i) swap(A[i], A[rev[i]]);
for (int l = 1; l < len; l <<= 1)
for (int i = 0; i < len; i += (l << 1))
for (int k = 0; k < l; ++k) {
int t1 = A[i + k], t2 = A[i + l + k] * (ll) wn[f][l + k] % P;
A[i + k] = (t1 + t2) % P;
A[i + l + k] = (t1 - t2 + P) % P;
}
if (f == 1) for (int inv = Pow(len, P - 2), i = 0; i < len; ++i) A[i] = A[i] * (ll) inv % P;
}

int f[MAXN];
int A[MAXN], B[MAXN] , J[MAXN] , ok[MAXN] , t[MAXN];

void CDQ(int *a, int l, int r) {
// cout << l << ' ' << r << endl;
if (l == r) return;
int m = l + r >> 1;
CDQ(a, l, m);
int p = 1, len = 0;
while (p <= (r - l + 1) * 2) p <<= 1, ++len;
getr(len);
for (int i = 0; i < p; ++i) A[i] = B[i] = 0;
for (int i = l; i <= m; ++i) if( ok[i] ) B[i - l] = ( 1ll * ( ( t[i] & 1 ) ? P-1 : 1 ) * f[i] ) % P;//, cout << B[i - l] <<' ';
for (int i = 0; i <= r - l; ++i) A[i] = J[i];// , cout << A[i] << ' ';
puts("");
NTT(A, p, 0), NTT(B, p, 0);
for (int i = 0; i < p; ++i) A[i] = 1ll * A[i] * B[i] % P;
NTT(A, p, 1);
for (int i = m + 1; i <= r; ++i) f[i] = (f[i] + 1ll * ( ( t[i] & 1 ) ? 1 : P-1 ) * A[i - l]) % P;
CDQ(a, m + 1, r);
}
}

int A[MAXN];
char s[MAXN];

int main() {
// freopen("in.in","r",stdin);
using namespace cdqntt;
J[0] = 1;for( int i = 1 ; i < MAXN ; ++ i ) J[i] = 1ll * J[i - 1] * Pow( i , P - 2 ) % P;
scanf("%s",s + 1);
n = strlen( s + 1 ) + 1;
int pre = 0;
for( int i = 1 ; i <= n ; ++ i ) {
if( s[i] == '>' ) ok[i] = 1;
t[i] = t[i - 1] + ok[i];
}
int l = 0 , len = 1;
while( len <= n + n ) len <<= 1 , ++ l;
getwn( l );
f[0] = 1 , ok[0] = 1;
CDQ( f , 0 , n );
cout << ( P - 1ll * f[n] * Pow( J[n] , P - 2 ) % P ) << endl;
}
文章作者: yijan
文章链接: https://yijan.co/old4/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog