[BZOJ5416][LOJ2719][NOI2018]冒泡排序[DP+组合数学]

\(S=\frac{1}{2}\sum_{i=1}^n|i-p_i|\),每次swap最好情况下会让 \(S\) 减少 \(2\),所以如果交换次数恰好是 \(\frac{1}{2}\sum_{i=1}^n|i-p_i|\),那么每个数都不能向 \(p_i\) 的反方向移动,也就是排序过程中,某个数字不能同时有往后和往前移动两个操作。

冒泡排序的过程是顺序比较每个数字和后面的数字,对于每个数字,如果往后移,前面肯定没有比它更大的数,所以这个数不会再往前移;如果往前移(前面有更大的数)同时后面有更小的数,这个数字就一定还会往后移。

所以一个序列不“好”的条件是存在某个位置 \(i\) 前面有比它更大的数,后面有比它更小的数,即这个序列中存在长度 \(>2\) 的下降子序列

不想写了,看 这篇博客吧(雾)

int a[MAXN >> 1], n;

inline Mod C(int n, int m) {
  if (m > n) return 0;
  return fac[n] * ifac[n - m] * ifac[m];
}
inline Mod Get(int n, int m) {
  return C(n + m - 2, m - 1) - C(n + m - 2, m - 2);//(1,1)走到(n,m)方案数
}

void init() {
  freopen("inverse.in", "r", stdin);
  freopen("inverse.out", "w", stdout);
  fac[0] = 1;
  lop1(i, MAXN) fac[i] = fac[i - 1] * i;
  ifac[MAXN - 1] = fac[MAXN - 1].inv();
  dlop0(i, MAXN - 1) ifac[i] = ifac[i + 1] * (i + 1);
  assert(ifac[0].a == 1);
}

void solve() {
  static bitset<600005>vis;
  n = in;
  lop1(i, n) in, a[i];
  int Min = 1, Max = 0;
  vis.reset();
  Mod ans = 0;
  lop1(i, n) {
    while (vis.test(Min)) ++Min;
    int t = n - i + 1, res = n - max(a[i], Max);//可行集合大小
    if (res > 1) ans += Get(t + 1, res);
    else ans += res > 0; //(1, 1)到(n, 1)
    if (Max > a[i] and a[i] > Min) break;//存在长度>2的下降子序列
    chmax(Max, a[i]);
    vis.set(a[i]);
  }
  out, ans.a, '\n';
}
posted @ 2019-02-23 11:17  QvvQ  阅读(141)  评论(0编辑  收藏  举报