#575. 「LibreOJ NOI Round #2」不等關係
阿新 • • 發佈:2020-09-02
首先只考慮 <
的限制,即 >
可以大於也可以小於,那麼實際上就是把序列分成幾個遞增序列,直接多重集排列即可。
現在考慮 >
的限制。設 \(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; }