题解:P3813 [FJOI2017] 矩阵填数
更好的阅读体验
注意到,一个矩阵最大值为
- 矩阵中的每一个元素
\le x 。 - 矩阵中存在至少一个
x 。
仅考虑第一个条件是好做的。具体地,每一个格子存在一个取值的上限
接下来加上第二个条件不好做,考虑把不符合条件的填数字方案给容斥掉。
由于
第二个式子之所以要
设
子集反演。
然后这道题就做完了。注意到我们有用的行和列只有矩形的边所在的行和列,所以可以离散化一下,这样我们暴力进行一次矩形修改的复杂度可以做到
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define N 41
#define MOD 1000000007
using namespace std;
int h,w,m,n,ans,bnx,bny,bx[N],by[N],l[N],r[N],u[N],d[N],val[N],mx[N][N];
int qpow(int x,int y=MOD-2)
{
int ret=1;
for(;y;x=x*x%MOD,y>>=1)if(y&1)ret=ret*x%MOD;
return ret;
}
void lsh(int &bn,int *b,int lim,int *x,int *y)
{
bn=0,b[++bn]=1,b[++bn]=lim+1;
for(int i=0;i<n;i++)b[++bn]=x[i],b[++bn]=y[i]+1;
sort(b+1,b+1+bn),bn=unique(b+1,b+1+bn)-b-1;
for(int i=0;i<n;i++)x[i]=lower_bound(b+1,b+1+bn,x[i])-b;
for(int i=0;i<n;i++)y[i]=lower_bound(b+1,b+1+bn,y[i]+1)-b-1;
}
void solve()
{
scanf("%lld%lld%lld%lld",&h,&w,&m,&n),ans=0;
for(int i=0;i<n;i++)
scanf("%lld%lld%lld%lld%lld",&u[i],&l[i],&d[i],&r[i],&val[i]);
lsh(bnx,bx,h,u,d),lsh(bny,by,w,l,r);
for(int S=0;S<(1<<n);S++)
{
int sum=1;
for(int i=1;i<=bnx;i++)
for(int j=1;j<=bny;j++)mx[i][j]=m;
for(int i=0;i<n;i++)
for(int j=u[i];j<=d[i];j++)
for(int k=l[i];k<=r[i];k++)mx[j][k]=min(mx[j][k],val[i]-(S>>i&1));
for(int i=1;i<bnx;i++)
for(int j=1;j<bny;j++)sum*=qpow(mx[i][j],(bx[i+1]-bx[i])*(by[j+1]-by[j])),sum%=MOD;
if(__builtin_popcountll(S)&1)ans+=MOD-sum;
else ans+=sum; ans%=MOD;
}
printf("%lld\n",ans);
}
main()
{
int T;scanf("%lld",&T);
while(T--)solve();
return 0;
}