1. 程式人生 > 實用技巧 >#575. 「LibreOJ NOI Round #2」不等關係

#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;
}