题意:给一个h*w(h,w<=10000)的二维矩阵,给定其中n(n<=10)个子矩阵的最大值v(v<=1000),求组成这个矩阵的方案数。
分析:由于h*w太大,而n,v比较小,此题可以从n,v下手。首先将大矩形离散化,就把大矩形分成啦数量很少的小矩形。对子矩阵的重叠部分,如果最大值不同,自然是取较小值。如果最大值相同,则比较麻烦,这时解题关键,需要通过容斥原理求方案数。例如:子矩阵a和子矩阵b最大值相等为v0,有重叠部分,求满足情况比较麻烦,可以求不满足情况。不满足情况为a不满足方案+b不满足方案-a和b同时不满足方案数。(基于ab的矩形内的数都小于等于v0)ans=总方案数-不满足情况,a不满足情况为((v0-1)^(num[a])*(v0)^(num[a并b]-num[a]) ),由此可以推至多个,用状压存储状态。所以就要分别求最大值相等的小矩形的总方案数。最后答案是所有方案数相乘。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
struct node
{
int x1,y1,x2,y2,v;
int siz() //返回面积
{
return (x2-x1)*(y2-y1);
}
} a[20];
LL POW(LL x,LL n,LL MOD)
{
LL ret=1;
while(n)
{
if(n&1)ret=ret*x%MOD;
x=x*x%MOD;
n>>=1;
}
return ret;
}
bool compare(const node&a,const node&b)
{
return a.x1<=b.x1&&a.y1<=b.y1&&a.x2>=b.x2&&a.y2>=b.y2;
}
int X[30],Y[30],V[30],id[30];
int jiao[12][4810],bing[12][4810];
vector<int>g[30];
int main()
{
int TA,h,w,m,n,hy,hx,hv,cas=1;
scanf("%d",&TA);
while(TA--)
{
memset(jiao,0,sizeof(jiao));
memset(bing,0,sizeof(bing));
scanf("%d%d%d%d",&h,&w,&m,&n);
hx=hy=hv=0;
X[++hx]=0;
X[++hx]=w;
Y[++hy]=0;
Y[++hy]=h;
for(int i=1; i<=n; i++)
{
scanf("%d%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2,&a[i].v);
a[i].x1--;
a[i].y1--;
X[++hx]=a[i].x1;
X[++hx]=a[i].x2;
Y[++hy]=a[i].y1;
Y[++hy]=a[i].y2;
V[++hv]=a[i].v;
}
sort(X+1,X+hx+1);
sort(Y+1,Y+hy+1);
sort(V+1,V+hv+1);
hx=unique(X+1,X+hx+1)-X-1;
hy=unique(Y+1,Y+hy+1)-Y-1;
hv=unique(V+1,V+hv+1)-V-1;
for(int i=1; i<=n; i++)
{
a[i].v=lower_bound(V+1,V+hv+1,a[i].v)-V;
id[i]=(int)g[a[i].v].size();
g[a[i].v].push_back(i);
}
int tm,siz;
LL all,out=0;
for(int i=1; i<hx; i++) //将小矩形归类
for(int j=1; j<hy; j++)
{
node tem=(node)
{
X[i],Y[j],X[i+1],Y[j+1],hv+1
};
tm=0;
for(int k=1; k<=n; k++)
if(compare(a[k],tem))
{
if(a[k].v<tem.v)
{
tem.v=a[k].v;
tm=1<<id[k];
}
else if(a[k].v==tem.v)
{
tm|=(1<<id[k]);
}
}
siz=tem.siz();
if(tem.v>hv)
out+=siz;
else
for(int k=tm; k; k=tm&(k-1))jiao[tem.v][k]+=siz;
}
for(int i=1; i<=hv; i++) //第一次容斥,求每个最大值所包含每种状态的交集大小
{
int tm=1<<(int)g[i].size();
for(int j=1; j<tm; j++)
{
for(int k=j; k; k=j&(k-1))
if(__builtin_popcount(k)&1)
bing[i][j]+=jiao[i][k];
else
bing[i][j]-=jiao[i][k];
}
}
LL sum,ans=POW((LL)m,out,MOD);
for(int i=1; i<=hv; i++) //第二次容斥,求方案数
{
int tm=(int)g[i].size();
tm=(1<<tm);
all=bing[i][tm-1];
sum=0;
for(int j=0; j<tm; j++)
if(__builtin_popcount(j)&1)
sum=(POW((LL)V[i]-1,(LL)bing[i][j],MOD)*POW((LL)V[i],all-bing[i][j],MOD)*(-1)+sum)%MOD;
else
sum=(POW((LL)V[i]-1,(LL)bing[i][j],MOD)*POW((LL)V[i],all-bing[i][j],MOD)+sum)%MOD;
ans=ans*sum%MOD;
}
ans=(ans+MOD)%MOD;
printf("Case #%d: %lld\n",cas++,ans);
for(int i=0;i<=hv;i++)g[i].clear();
}
return 0;
}