FZOJ4092 Determinant

\(\texttt{Description}\)

有一个 \(n\times n\) 的矩阵,初始全为 \(0\)。有 \(n\) 次操作,每次会选择一个 \((x_{i1},y_{i1})\sim(x_{i2},y_{i2})\) 的矩阵中的一点加 \(1\),求矩阵行列式期望。
\((n\le 10^5)\),同余 \(10^9+7\)

\(\texttt{Solution}\)

对于排列 \(p\) 定义 \(\operatorname{sign}(p):=(-1)^{\sum_{i<j}[p_i>p_j]}\)

考虑 \(n\) 个点最终的坐标 \((p_1,q_1),(p_2,q_2),\cdots,(p_n,q_n)\),贡献为 \(\operatorname{sign}(p\otimes q)\),此时 \(\otimes\) 表示置换。存在 \(\operatorname{sign}(p\otimes q)=\operatorname{sign}(p)\times\operatorname{sign}(q)\)
所以答案是

\[\operatorname{E}\left(\sum_{p:p_i\in[x_{i1},x_{i2}]}\sum_{q:q_i\in[y_{i1},y_{i2}]}\operatorname{sign}(p\otimes q)\right)=\operatorname{E}\left(\sum_{p:p_i\in[x_{i1},x_{i2}]}\operatorname{sign}(p)\right)\times\operatorname{E}\left(\sum_{q:q_i\in[y_{i1},y_{i2}]}\operatorname{sign}(q)\right) \]

现在考虑计算 \(\operatorname{E}\left(\sum_{p:p_i\in[l_i,r_i]}\operatorname{sign}(p)\right)\),发现此式等于 \(\dfrac{\operatorname{det}A}{\prod(r_i-l_i+1)}\),其中 \(A\) 满足对于任意第 \(i\) 行上有且仅有区间 \([l_i,r_i]\)\(1\),其余全为 \(0\)

考虑如何计算这种特殊的矩阵的行列式:模拟高斯消元的过程,用 \(S_i\) 表示区间左端点为 \(i\) 的右端点的集合,每次取出当前集合的最小值 \(x\),并将 \(S_i\) 合并入 \(S_{x+1}\)。其间对于行列式的值用一个变量维护一下。以上我们维护一个数据结构,支持集合合并,集合取最小值,用可并堆即可。

#include <iostream>
#include <functional>

using i64 = long long;

const i64 MOD = 1000000007;
const int N = 100000 + 7;

i64 pow(i64 a, i64 k) {
  i64 t = 1;
  for (; k; a = a * a % MOD, k >>= 1)
    if (k & 1) t = t * a % MOD;
  return t;
}

i64 solve(int n, int l[], int r[]) {
  static int val[N], kid[N][2], rt[N];
  static int id[N], rw[N];
  std::function<int(int, int)> merge = [&](int x, int y) {
    if (!x || !y) return x ^ y;
    if (val[x] > val[y]) std::swap(x, y);
    kid[x][0] = merge(kid[x][0], y);
    std::swap(kid[x][0], kid[x][1]);
    return x;
  };

  i64 ans = 0;
  for (int i = 1; i <= n; ++i) kid[i][0] = kid[i][1] = rt[i] = 0;
  for (int i = 1; i <= n; ++i) val[i] = r[i], rw[i] = id[i] = i, rt[l[i]] = merge(rt[l[i]], i);
  for (int i = 1; i <= n; ++i) {
    if (!rt[i] || val[rt[i]] < i) return 0;
    if (id[rt[i]] != i) ans ^= 1, id[rw[i]] = id[rt[i]], rw[id[rt[i]]] = rw[i];
    if (val[rt[i]] + 1 <= n)
      rt[val[rt[i]] + 1] = merge(rt[val[rt[i]] + 1], merge(kid[rt[i]][0], kid[rt[i]][1]));
  }
  ans = ans & 1 ? MOD - 1 : 1;
  for (int i = 1; i <= n; ++i) ans = ans * (r[i] - l[i] + 1) % MOD;
  return pow(ans, MOD - 2);
}

int main() {
  static int t, n, l1[N], r1[N], l2[N], r2[N];
  scanf("%d", &t);
  while (t--) {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) scanf("%d%d%d%d", l1 + i, r1 + i, l2 + i, r2 + i);
    printf("%lld\n", solve(n, l1, r1) * solve(n, l2, r2) % MOD);
  }
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/Ryedii-blog/p/12594488.html
今日推荐