有一个 的矩阵,刚开始所有位置都为 。
接下来进行 次操作,第 次会在左上角为 ,右下角为 的矩形中等概率随机选择一个位置将其 。
求操作完后矩阵行列式的期望值,对 取模。
的矩阵 的行列式 ,其中 为 的排列的集合, 为 的逆序对个数。
。
首先转成计数,只需要最后乘上 即可。
设最终矩阵为 ,则不难发现由于 ,所以若两次操作加到了同一个位置则行列式为 。
设第 次加在了 ,设 和 分别表示 是否在 和 意义上构成逆序对,则不难发现 在 意义上构成逆序对当且仅当 ,而一个逆序对的贡献是 ,所以两维可以独立做。
那么现在问题变为求 。
不难发现,若存在 ,则将 交换会让逆序对个数改变 ,且交换后还合法,所以会互相抵消。
那么每次选择 最小的区间中 最小的那个 ,让 ,再将所有 的 都变为 ,不难发现这样填出来的方案一定合法,且根据这个构造方法不难看出合法方案最多只有一种,所以有一个有趣的性质就是 。
这个贪心用可并堆维护即可,时间复杂度 。
代码如下:
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int S=100005,p=1000000007;
template<typename T,int siz>
class LTree
{
private:
struct node
{
T val;
int dist,ls,rs;
}tr[siz];
int tot;
inline int nnde(T val)
{
tr[++tot]={val,1,0,0};
return tot;
}
inline void upda(int u)
{
int &ls=tr[u].ls,&rs=tr[u].rs;
if(tr[ls].dist>tr[rs].dist) swap(ls,rs);
tr[u].dist=tr[ls].dist+1;
}
int merge(int x,int y)
{
if(x==0||y==0) return x+y;
if(tr[x].val>tr[y].val) swap(x,y);
tr[x].ls=merge(tr[x].ls,y);
upda(x);
return x;
}
public:
inline void clear()
{
tr[tot=0]=(node){T(),0,0,0};
}
inline void ins(int &rt,T x)
{
rt=merge(rt,nnde(x));
}
inline T top(int rt)
{
return tr[rt].val;
}
inline void pop(int &rt)
{
rt=merge(tr[rt].ls,tr[rt].rs);
}
inline void meg(int &x,int y)
{
x=merge(x,y);
}
};
struct node
{
int xl,xr,yl,yr;
}a[S];
int n;
LTree<pair<int,int>,S> tr;
int rt[S];
int rex[S],rey[S];
int b[S],c[S];
inline void add(int p,int x)
{
for(int i=p;i<=n;i+=i&-i) c[i]+=x;
}
inline int que(int p)
{
int res=0;
for(int i=p;i>=1;i-=i&-i) res+=c[i];
return res;
}
inline int qpow(int x,int y)
{
int res=1;
for(;y>0;y>>=1,x=1ll*x*x%p) res=y&1?1ll*res*x%p:res;
return res;
}
inline void slove()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d%d%d",&a[i].xl,&a[i].xr,&a[i].yl,&a[i].yr);
tr.clear();
for(int i=1;i<=n;i++) rt[i]=0;
for(int i=1;i<=n;i++) tr.ins(rt[a[i].xl],make_pair(a[i].xr,i));
// for(int i=1;i<=n;i++)
// {
// printf("%d %d %d %d\n",a[i].xl,a[i].xr,a[i].yl,a[i].yr);
// auto pre=tr.top(rt[i]);
// printf("%d: %d\n",pre.second,pre.first);
// }
for(int i=1;i<=n;i++)
{
auto pre=tr.top(rt[i]);
tr.pop(rt[i]);
int id=pre.second,r=pre.first;
if(r<i) return puts("0"),void();
rex[id]=i;
tr.meg(rt[r+1],rt[i]);
}
// puts("FINX");
tr.clear();
for(int i=1;i<=n;i++) rt[i]=0;
for(int i=1;i<=n;i++) tr.ins(rt[a[i].yl],make_pair(a[i].yr,i));
for(int i=1;i<=n;i++)
{
auto pre=tr.top(rt[i]);
tr.pop(rt[i]);
int id=pre.second,r=pre.first;
if(r<i) return puts("0"),void();
rey[id]=i;
tr.meg(rt[r+1],rt[i]);
}
for(int i=1;i<=n;i++) b[rex[i]]=rey[i];
for(int i=1;i<=n;i++) c[i]=0;
int res=1;
for(int i=n;i>=1;i--)
{
res*=que(b[i]-1)&1?-1:1;
add(b[i],1);
}
if(res<0) res+=p;
for(int i=1;i<=n;i++)
{
int pre=1ll*(a[i].xr-a[i].xl+1)*(a[i].yr-a[i].yl+1)%p;
res=1ll*res*qpow(pre,p-2)%p;
}
printf("%d\n",res);
}
int main()
{
freopen("math.in","r",stdin);
freopen("math.out","w",stdout);
int T;
scanf("%d",&T);
while(T-->0) slove();
return 0;
}