#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;
}
posted @ 2020-09-02 20:15  JiaZP  阅读(171)  评论(0编辑  收藏  举报