Loading

AGC030D 题解

题目链接

是的,对我来说又是一道不可做题。。。

看到题目后对着题面发了 40 min 的呆,光荣想到 \(O(n2^q)\) 的指数级做法。
然后灵光一现,发现可能直接做做不出来,然后就花一天的时间胡了一个做法。

我们要求的是所有可能的逆序对的个数和,但这样显然是不对的。
我们考虑先求出进行任意操作后的逆序对的期望个数 \(Ans\) ,答案直接乘上 \(2^q\) 就可以了。
接着我们再拆分贡献,计算每两位 \(i\)\(j\) 满足 \(i<j\) 并且 \(a_i>a_j\) 的期望。
那么总的期望就是所有上述情况的期望的总和。

我们设 \(\tt DP\) 状态 \(F(i,j)\) 表示第 \(i\) 位比第 \(j\) 位小的期望。
初始化非常简单 F[i][j] = (a[i] < a[j]); 最开始情况就是原数组的情况。

接下来枚举每一个可以交换的操作,为了方便我们把原题中的 \(x_i\)\(y_i\)\(l\)\(r\) 表示。
可以发现,有 \(\frac{1}{2}\) 的概率会进行交换,所以最简单的一步是。

\[F(l,r)=F(r,l)=\frac{1}{2}\times(F(l,r)+F(r,l)) \]

接下来我们考虑只有一位是 \(l\)\(r\) 的二元组 \((i,j)\)
很显然的是对于这样的二元组,可以按照上述的思路继续转移,因为不好描述,所以直接放代码。

for (int i = 1; i <= q; i++) {
  int l = x[i], r = y[i];
  f[l][r] = f[r][l] = inv * (f[l][r] + f[r][l]) % mod;
  for (int j = 1; j <= n; j++) {
    if (j == l || j == r) continue;
    f[l][j] = f[r][j] = inv * (f[l][j] + f[r][j]) % mod;
    f[j][l] = f[j][r] = inv * (f[j][l] + f[j][r]) % mod;
  }
}

其中 \(\tt inv\) 表示 \(2\) 在模题目中模数时的逆元。
统计答案的话,直接按照 \(\tt DP\) 的状态枚举累加就可以了,能看懂状态的话就不难。
注意最后要乘上 \(2^q\) 求可以了。

#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <string>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>

#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)
#define quad putchar(' ')
#define Enter putchar('\n')

using std::abs;
using std::pair;
using std::string;
using std::make_pair;

#define int long long

template <class T> void read(T &a);
template <class T> void write(T x);

template <class T, class ...rest> void read(T &a, rest &...x);
template <class T, class ...rest> void write(T x, rest ...a);

const int N = 3005;
const int mod = 1e9 + 7;

int n, q, a[N], x[N], y[N], f[N][N];

inline int power(int a, int n) {
  int ret = 1;
  while (n) {
    if (n & 1) ret = ret * a % mod;
    a = a * a % mod; n /= 2;
  } return ret;
}

signed main(void) {
  read(n, q);
  for (int i = 1; i <= n; i++) read(a[i]);
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++) f[i][j] = (a[i] < a[j]);
  for (int i = 1; i <= q; i++) read(x[i], y[i]);
  int mul = power(2, q);
  int inv = (mod + 1) / 2;
  for (int i = 1; i <= q; i++) {
    int l = x[i], r = y[i];
    f[l][r] = f[r][l] = inv * (f[l][r] + f[r][l]) % mod;
    for (int j = 1; j <= n; j++) {
      if (j == l || j == r) continue;
      f[l][j] = f[r][j] = inv * (f[l][j] + f[r][j]) % mod;
      f[j][l] = f[j][r] = inv * (f[j][l] + f[j][r]) % mod;
    }
  }
  int ans = 0;
  for (int i = 1; i <= n; i++) 
    for (int j = 1; j <= i - 1; j++) ans = (ans + f[i][j]) % mod;
  write(ans * mul % mod); Enter;
  return 0;
}

template <class T> void read(T &a) {
  int s = 0, t = 1;
  char c = getchar();
  while (!isdigit(c) && c != '-') c = getchar();
  if (c == '-') c = getchar(), t = -1;
  while (isdigit(c)) s = s * 10 + c - '0', c = getchar();
  a = s * t;
}
template <class T> void write(T x) {
  if (x == 0) putchar('0');
  if (x < 0) putchar('-'), x = -x;
  int top = 0, sta[50] = {0};
  while (x) sta[++top] = x % 10, x /= 10;
  while (top) putchar(sta[top] + '0'), top --;
  return ;
}

template <class T, class ...rest> void read(T &a, rest &...x) {
  read(a); read(x...);
}
template <class T, class ...rest> void write(T x, rest ...a) {
  write(x); quad; write(a...);
}

posted @ 2022-07-19 20:28  Aonynation  阅读(24)  评论(0编辑  收藏  举报