\(\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[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;
}