「LibreOJ NOI Round #2」不等关系
「LibreOJ NOI Round #2」不等关系
暑假 dls 讲过但是当时掉线了。。
考虑我们如果直接无视 > 符号,现在这个排列就一定是一些下降的子段组合在一起的。我们假设这些段的长度分别是 $ a_1,a_2,\dots ,a_k $ 那么算出来方案数量是
\[\frac{n!}{a_1!a_2!\dots a_k!}
\]
因为我们可以任意定一个排列,为了满足条件只需要把这些子段排个序。理解一下发现就是这个东西。
但是我们还有 > 符号啊,既然不好处理,我们可以考虑容斥,比如, <<><> 这个序列,它的答案就可以推出来
<<><> = <<*<* - <<<<* - <<*<< + <<<<<
为啥是对的?我们可以把每个位置拖出来,发现 这个位置是 > = 这个位置关系任意 - 这个位置是 >
所以我们可以考虑用 dp 做这个容斥, $ dp[i] $ 表示前 $ i $ 个关系被满足的方案数量,则有
\[dp[i] = i![s_i='>']\sum_j [s_j='>'](-1)^{cnt_i-cnt_j-1}\frac{1}{(i-j)!} \times dp[j]
\]
这个东西是可以方便的分治FFT的,化开就是:
\[dp[i] = i!(-1)^{c_i}\sum_{j+k=i} [s_j='>']-1^{c_j+1}dp[j] \times \frac{1}{k!}
\]
写的时候不知道哪里系数犯了,算出来是负的。。。
#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;
}