决心 - 行列式 - 贪心 - 可并堆

题目大意:一个n*n个矩阵,以及n个矩形 x 1 , x 2 , y 1 , y 2 x_1,x_2,y_1,y_2 x1,x2,y1,y2,然后每个矩形等概率随机其内部一个点点权+1(一开始全是0),问最后行列式的期望。 n ≤ 1 0 5 n\le10^5 n105
题解:
(一开始以为是m个矩形然后就开始懵逼我连 x 1 = x 2 , y 1 = y 2 x_1=x_2,y_1=y_2 x1=x2,y1=y2都不会……)
因为有n个矩形所有只有n个位置有值所以这n个位置必须是个排列答案才不是0(废话但是没这个就不会做啦)

先考虑 x 1 = x 2 , y 1 = y 2 x_1=x_2,y_1=y_2 x1=x2,y1=y2怎么做,判定x和y是否是两个排列再统计逆序对数量奇偶性即可(废话)。

然后考虑 x 1 = x 2 x_1=x_2 x1=x2怎么做,首先这个时候x必须是个排列(否则某一行没法选byebye),然后任何一种情况出现的概率是相同的(都是 1 ∏ i = 1 n Δ y i \frac{1}{\prod_{i=1}^n\Delta y_i} i=1nΔyi1),因此问题转化为,对一个每行是一段连续的1的矩阵求行列式。
这个怎么做,其实很简单,考虑朴素的 O ( n 3 ) O(n^3) O(n3)的行列式,从小到大枚举每一列,然后随便选取在这一列上有值的某行a,然后剩下这一列上有值的行b要减去a这一行。

考虑现在有任意一行是一段连续的1这个性质,因此每一列都选择这一列有值的行中右端点最小的,假设这个右端点是r,这样剩余的行减去这一行相当于是这些行的左端点变成了r+1。
上述部分随便用个什么可并堆或者线段树合并之类的即可。

然后最终的问题:
反过来考虑,即给每个矩形分配一个行x和列y,使得(x,y)在矩形内,并且x和y分别是排列,然后(行列式绝对值显然会是1)统计行列式正负即可。
然后发现这两部分几乎是独立的:你可以先分配行的排列,然后分配列的排列,然后看满足 x i &lt; x j , y i &gt; y j x_i&lt;x_j,y_i&gt;y_j xi<xj,yi>yj ( i , j ) (i,j) (i,j)的数量的奇偶性,然后 [ x i &lt; x j   a n d   y i &gt; y j ] = [ i &lt; j   a n d   y i &gt; y j ]   x o r   [ i &gt; j   a n d   x i &lt; x j ] [x_i&lt;x_j\ \mathrm{and}\ y_i&gt;y_j]=[i&lt;j\ \mathrm{and}\ y_i&gt;y_j]\ \mathrm{xor}\ [i&gt;j\ \mathrm{and}\ x_i&lt;x_j] [xi<xj and yi>yj]=[i<j and yi>yj] xor [i>j and xi<xj],因此可以完全分成两部分统计。(所以其实就是 x 1 = x 2 x_1=x_2 x1=x2的做法然后写两遍乘起来啊QwQ)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define mod 1000000007
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
namespace INPUT_SPACE{
	const int BS=(1<<24)+5;char Buffer[BS],*HD,*TL;
	char gc() { if(HD==TL) TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);return (HD==TL)?EOF:*HD++; }
	inline int inn()
	{
		int x,ch;while((ch=gc())<'0'||ch>'9');
		x=ch^'0';while((ch=gc())>='0'&&ch<='9')
			x=(x<<1)+(x<<3)+(ch^'0');return x;
	}
}using INPUT_SPACE::inn;
inline int fast_pow(int x,int k,int ans=1) { for(;k;k>>=1,x=(lint)x*x%mod) (k&1)?ans=(lint)ans*x%mod:0;return ans; }
const int N=100003;
namespace QwQ{
	int id[N],p[N],node_cnt,val[N],lef[N],rig[N],dis[N];
	inline int new_node(int v,int d)
	{
		int x=++node_cnt;dis[x]=0;
		val[x]=v,p[id[x]=d]=x;
		return lef[x]=rig[x]=0,x;
	}
	int merge_lst(int x,int y)
	{
		if(!x||!y) return x+y;
		if(val[x]>val[y]) swap(x,y);
		rig[x]=merge_lst(rig[x],y);
		if(dis[rig[x]]>dis[lef[x]]) swap(lef[x],rig[x]);
		if(rig[x]) dis[x]=dis[rig[x]]+1;else dis[x]=0;
		return x;
	}
	struct lst{
		int rt;
		inline int init() { return rt=0; }
		inline int merge(const lst &t) { return rt=merge_lst(rt,t.rt); }
		inline int pop() { return rt=merge_lst(lef[rt],rig[rt]); }
		inline int topv() { return val[rt]; }
		inline int topid() { return id[rt]; }
		inline int empty() { return !rt; }
		inline int insert(int v,int id) { return rt=merge_lst(rt,new_node(v,id)); }
	}t[N];
	inline int solve(pii *ps,int n)
	{
		node_cnt=0;rep(i,1,n) t[i].init();int ans=1,xs=1,l,r,v,d;
		rep(i,1,n) t[l=ps[i].fir].insert(r=ps[i].sec,i),xs=xs*(r-l+1ll)%mod;
		rep(i,1,n)
		{
			while(!t[i].empty()&&t[i].topv()<i) t[i].pop();
			if(t[i].empty()) return 0;
			v=t[i].topv(),d=t[i].topid(),t[i].pop();
			if(v<n) t[v+1].merge(t[i]);
			if(d^i) ans*=-1,p[id[p[i]]=d]=p[i];
		}
		if(ans<0) ans+=mod;
		return (lint)ans*fast_pow(xs,mod-2)%mod;
	}
}using QwQ::solve;
pii p1[N],p2[N];
int main()
{
	for(int T=inn(),n;T;T--)
	{
		n=inn();
		rep(i,1,n) p1[i].fir=inn(),p1[i].sec=inn(),p2[i].fir=inn(),p2[i].sec=inn();
		printf("%lld\n",(lint)solve(p1,n)*solve(p2,n)%mod);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值