给定一个 (h imes w) 的矩阵,每个格子中将填入 (1) 到 (m) 中的某个整数。
一个合法的填数方案须满足 (n) 条限制,每条限制形如“以 ((x_1,y_1)) 为左上角,((x_2,y_2)) 为右下角的子矩阵中,最大值必须为 (v)”。
求填数方案数,对大质数取模。
把这 (n) 条限制按照 (v) 从小到大排序。
这样,就可以对每个 (x) 求出最小限制为 (x) 的区域的答案,最后处理没被限制的区域(这显然是 (m^{S_R}),(S_R) 没被限制的区域的面积)。
答案就是把这些东西全部乘起来。
接下来考虑对于每个 (x) 求出最小限制为 (x) 的区域的答案。
设 (A) 为所有 (v) 值等于 (x) 的限制构成的集合。我们要满足 (A) 中所有限制,并不好算,考虑容斥。
改为对 (A) 的每个子集 (B),求强制让其不满足的方案数,大概是 ((x-1)^{S_B}x^{S_{A-B}}) ,其中 (S_A) 为集合 (A) 中所有子矩阵的并的面积去掉其中有更小限制的面积。然后加加减减就行。
发现我们还要求区域面积,当然可以离散化坐标值然后随便搞,但那个细节巨多。考虑更好写的方法:继续容斥。
我们发现本题中所有的面积都可以转为矩形的交和并。
发现矩形交是好求的,而并的就等于其子集的交加加减减,然后就求完了。枚举子集的子集是 (O(3^n)) 的。
每组数据的复杂度为 (O(3^n+2^nnlog(hw))),(log) 来自快速幂。(当然可以 FMT 把 (3^n) 优化掉,优化到 (O(2^nnlog(hw))),但没有必要)。
#include<cstdio>
#include<algorithm>
typedef long long ll;
const int N=10,M=1e9+7;
inline int Pow(int a,int m){int s=1;for(;m;m>>=1)m&1?s=(ll)s*a%M:0,a=(ll)a*a%M;return s;}
int x[N],y[N],xx[N],yy[N],mx[N],t[N],n,m,h,w,cnt[1<<N],ans,tmp;ll cup[1<<N],cap[1<<N];
bool Cmp(const int&i,const int&j){return mx[i]<mx[j];}
int main(){
int a,b,aa,bb,U;
for(int I=0;I<(1<<N);I++)cnt[I]=cnt[I>>1]+(I&1);
int T;scanf("%d",&T);for(;T--;){
scanf("%d%d%d%d",&h,&w,&m,&n);
for(int i=0;i<n;i++)scanf("%d%d%d%d%d",x+i,y+i,xx+i,yy+i,mx+i),t[i]=i;
std::sort(t,t+n,Cmp);
for(int I=0;I<(1<<n);I++){
a=b=0,aa=h,bb=w;
for(int i=0;i<n;i++)if(I&1<<i){
a=std::max(a,x[i]);
b=std::max(b,y[i]);
aa=std::min(aa,xx[i]);
bb=std::min(bb,yy[i]);
}
cap[I]=a>aa||b>bb?0:(ll)(aa-a+1)*(bb-b+1);
}
for(int I=0;I<(1<<n);I++){
cup[I]=0;
for(int J=I;J;J=I&J-1)
cup[I]=(cup[I]+cap[J]*(cnt[J]&1?1:-1));
}
ans=Pow(m,h*w-cup[(1<<n)-1]);
U=0;
for(int l=0,r=0,I;r<n;r++)if(r+1==n||mx[t[r]]!=mx[t[r+1]]){
I=0;
for(int i=l;i<=r;i++)I|=1<<t[i];
tmp=Pow(mx[t[r]],cup[I|U]-cup[U]);
for(int J=I;J;J=I&J-1)
tmp=(tmp+(ll)Pow(mx[t[r]]-1,cup[J|U]-cup[U])*Pow(mx[t[r]],cup[I|U]-cup[J|U])%M*(cnt[J]&1?-1:1)+M)%M;
ans=(ll)ans*tmp%M;
U|=I,l=r+1;
}
printf("%d
",ans);
}return 0;
}