#575. 「LibreOJ NOI Round #2」不等关系
首先只考虑 <
的限制,即 >
可以大于也可以小于,那么实际上就是把序列分成几个递增序列,直接多重集排列即可。
现在考虑 >
的限制。设 \(f_i\) 表示前 \(i\) 个的答案,那么我们先不管最后一个 >
的限制求个方案数,然后我们多算了那个 >
实际填 <
的情况,那么我们需要减去把那个 >
当成 <
的方案。但是我们减这个方案的时候还会多减一些,需要把前面的前面的那个 >
当成 <
的情况加上...
\[\Large f_i = \sum_{j=0}^{i-1}[s_j='>']f_j{i \choose j}(-1)^{\sum_{k=j+1}^i[s_k='>']}
\]
拆开组合数以后就可以分治 NTT 了。
这题主要神仙在选取一个“标准点”——最后一个 >
是否合法,然后就是一直推下去了。
关键代码:
inline int cc(int x) { return x & 1 ? -1 : 1; }
ll A[N], B[N];
ll f[N], g[N], h[N];
void sol(int L, int R) {
if (!R) return ;
if (L == R) return f[L] = f[L] * cc(cnt[L - 1]) * jie[L] % P, h[L] = (s[L] == '>') * f[L] * cc(cnt[L]) * jieni[L] % P, void();
int mid = (L + R) >> 1;
sol(L, mid);
int len = (R - L + 1);
for (register int i = 0; i < len; ++i) A[i] = g[i];
for (register int i = 0; i <= mid - L; ++i) B[i] = h[i + L];
len += (mid - L + 1);
int limi = 1, Len = 0;
while (limi <= len) limi <<= 1, ++Len;
for (register int i = 0; i < limi; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (Len - 1));
ntt(A, 1, limi), ntt(B, 1, limi);
for (register int i = 0; i < limi; ++i) A[i] = A[i] * B[i] % P, B[i] = 0;
ntt(A, -1, limi);
for (register int i = mid + 1; i <= R; ++i) f[i] = (f[i] + A[i - L]) % P;
for (register int i = 0; i <= limi; ++i) A[i] = 0;
sol(mid + 1, R);
}
int main() {
scanf("%s", s + 1); n = strlen(s + 1) + 1;
s[0] = '>'; cnt[0] = 1;
for (register int i = 1; i <= n; ++i) cnt[i] = cnt[i - 1] + (s[i] == '>');
init();
f[0] = 1; h[0] = -1;
for (register int i = 1; i <= n; ++i) g[i] = jieni[i];
sol(0, n);
printf("%lld\n", (f[n] % P + P) % P);
return 0;
}