题解 P3813 【[FJOI2017]矩阵填数】
一、题目
点此看题
二、解法
观察数组范围,发现
怎么个离散化法呢?考虑将每一个限制的四个边延长,这样就将大矩形切割成了至多
也就是我们把
离散化后,我们发现每个限制中只要中只要有一个小矩形满足限制就可以了,由于限制数很小,考虑状态压缩,设
中间的那个小矩形被两个限制所包含,但是我们只能满足要求最大值为
最后输出
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int MOD = 1e9+7;
const int MAXN = 10005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int T,h,w,m,n,cnt,oth,dp[505][1<<10];
int x1[MAXN],y1[MAXN],x2[MAXN],y2[MAXN],v[MAXN];
bool vx[MAXN][2],vy[MAXN][2];
vector<pair<int,int> > x,y;
struct node
{
int f,g,s;
}a[505];
void add(int z,bool d,bool type)
{
if(!type) x.push_back(make_pair(z,d)),vx[z][d]=1;
else y.push_back(make_pair(z,d)),vy[z][d]=1;
}
int qkpow(int a,int b)
{
int res=1;
while(b>0)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
signed main()
{
T=read();
while(T--)
{
h=read();w=read();m=read();n=read();
cnt=oth=0;
memset(vx,0,sizeof vx);
memset(vy,0,sizeof vy);
memset(dp,0,sizeof dp);
x.clear();y.clear();
add(1,0,0);add(h,1,0);
add(1,0,1);add(w,1,1);
for(int i=1;i<=n;i++)
{
x1[i]=read();y1[i]=read();x2[i]=read();y2[i]=read();v[i]=read();
if(x1[i]>1 && !vx[x1[i]-1][1])
add(x1[i]-1,1,0);
if(!vx[x1[i]][0])
add(x1[i],0,0);
if(x2[i]<h && !vx[x2[i]+1][0])
add(x2[i]+1,0,0);
if(!vx[x2[i]][1])
add(x2[i],1,0);
if(y1[i]>1 && !vy[y1[i]-1][1])
add(y1[i]-1,1,1);
if(!vy[y1[i]][0])
add(y1[i],0,1);
if(y2[i]<w && !vy[y2[i]+1][0])
add(y2[i]+1,0,1);
if(!vy[y2[i]][1])
add(y2[i],1,1);
}
sort(x.begin(),x.end());
sort(y.begin(),y.end());
for(int i=0;i<x.size();i+=2)
for(int j=0;j<y.size();j+=2)
{
int a1=x[i].first,a2=x[i+1].first;
int b1=y[j].first,b2=y[j+1].first;
int siz=(a2-a1+1)*(b2-b1+1),s=0,Min=m;
for(int k=1;k<=n;k++)
{
if(a1>=x1[k] && a2<=x2[k] && b1>=y1[k] && b2<=y2[k])
{
if(Min>v[k])
{
Min=v[k];
s=0;
}
if(Min==v[k]) s|=(1<<k-1);
}
}
int g=qkpow(Min-1,siz),f=qkpow(Min,siz)-g;
if(!s) oth+=siz;
else a[++cnt]=node{f,g,s};
}
dp[0][0]=1;
for(int i=1;i<=cnt;i++)
{
for(int j=0;j<(1<<n);j++)
dp[i][j|a[i].s]=(dp[i][j|a[i].s]+dp[i-1][j]*a[i].f)%MOD;
for(int j=0;j<(1<<n);j++)
dp[i][j]=(dp[i][j]+dp[i-1][j]*a[i].g)%MOD;
}
printf("%d\n",(dp[cnt][(1<<n)-1]*qkpow(m,oth)%MOD+MOD)%MOD);
}
}