您的位置:首页 > 其它

【BZOJ5010】【FJOI2017】矩阵填数 [状压DP]

2017-09-16 10:23 309 查看

矩阵填数

Time Limit: 10 Sec  Memory Limit: 128 MB
[Submit][Status][Discuss]

Description

  给定一个 h*w 的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w。   在这个矩阵中你需要在每个格子中填入 1..m 中的某个数。   给这个矩阵填数的时候有一些限制,给定 n 个该矩阵的子矩阵,以及该子矩阵的最大值 v,要求你所填的方案满足该子矩阵的最大值为 v。   现在,你的任务是求出有多少种填数的方案满足 n 个限制。   两种方案是不一样的当且仅当两个方案至少存在一个格子上有不同的数。   由于答案可能很大,你只需要输出答案对 1,000,000,007 的取模即可。

Input

  输入数据的第一行为一个数 T,表示数据组数。   对于每组数据,第一行为四个数 h,w,m,n。   接下来 n 行,每一行描述一个子矩阵的最大值 v。   每行为五个整数 x1,y1,x2,y2,v,表示一个左上角为(x1,y1),右下角为(x2,y2)的子矩阵的最大值为 v 。

Output

  对于每组数据输出一行,表示填数方案 mod 1,000,000,007 后的值。

Sample Input

  2
  3 3 2 2
  1 1 2 2 2
  2 2 3 3 1
  4 4 4 4
  1 1 2 3 3
  2 3 4 4 2
  2 1 4 3 2
  1 2 3 4 4

Sample Output

  28
  76475

HINT

  T≤5, 1≤h,w,m≤10000, 1≤v≤m, 1≤n≤10

Main idea

  给定一个矩阵,要求若干个子矩阵中最大值必须为Val,询问方案数。

Solution

  显然我们想到了状压DP,令 f[i][j] 表示做到了第i个块状态为j的方案,j表示哪些块满足限制

  由于子矩阵限制可能会重叠,所以我们先预处理,将矩阵分为若干个小块每个小块中仅有一个限制条件(显然就是所有覆盖条件中最小的一个)。

  然后我们记 Val 表示这一块里面的限制值Num 表示这一块的个数,然后我们再记个 op 表示覆盖哪些块的限制值为Val

  之后用状压DP,考虑第 i 块是否取限制值则方案数为 (Val - 1) ^ Num不取则方案数为 Val ^ Num - (Val - 1) ^ Num

  当取限制值时,把对应方案数转移到 f[i + 1][j | op[i + 1]]否则转移到 f[i + 1][j]。最后答案就是 f[cnt][all] 了。

Code

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<map>
using namespace std;
typedef long long s64;

const int ONE=505;
const int MOD=1e9+7;
const int INF=2147483640;

int T;
int h,w,m,n,all;
int qx[ONE],x_num,qy[ONE],y_num;
int Num[ONE],Val[ONE],op[ONE],cnt;
int f[ONE][3001];

struct power
{
int x1,y1;
int x2,y2;
int val;
}a[ONE];

int get()
{
int res=1,Q=1;char c;
while( (c=getchar())<48 || c>57 )
if(c=='-')Q=-1;
res=c-48;
while( (c=getchar())>=48 && c<=57 )
res=res*10+c-48;
return res*Q;
}

s64 Quick(s64 a,int b)
{
s64 res=1;
while(b)
{
if(b&1) res=res*a%MOD;
a=(s64)a*a%MOD;
b>>=1;
}
return res;
}

void Deal_first()
{
sort(qx+1,qx+x_num+1);  x_num=unique(qx+1,qx+x_num+1)-qx-1;
sort(qy+1,qy+y_num+1);  y_num=unique(qy+1,qy+y_num+1)-qy-1;

cnt=0;
for(int i=2;i<=x_num;i++)
for(int j=2;j<=y_num;j++)
{
int lenx=qx[i]-qx[i-1];
int leny=qy[j]-qy[j-1];
Num[++cnt]=lenx*leny;   Val[cnt]=m; op[cnt]=0;

for(int l=1;l<=n;l++)
if(a[l].x1<=qx[i-1] && qx[i]<=a[l].x2 && a[l].y1<=qy[j-1] && qy[j]<=a[l].y2)
Val[cnt]=min(Val[cnt],a[l].val);

for(int l=1;l<=n;l++)
if(a[l].val==Val[cnt])
if(a[l].x1<=qx[i-1] && qx[i]<=a[l].x2 && a[l].y1<=qy[j-1] && qy[j]<=a[l].y2)
op[cnt]|=(1<<l-1);
}
}

void Deal()
{
memset(f,0,sizeof(f));
f[0][0]=1;

for(int i=0;i<=cnt-1;i++)
for(int opt=0;opt<=all;opt++)
if(f[i][opt])
{
f[i+1][opt|op[i+1]] = (f[i+1][opt|op[i+1]] + (s64)f[i][opt]*(s64)(Quick(Val[i+1],Num[i+1]) - Quick(Val[i+1]-1,Num[i+1]) + MOD) % MOD) % MOD;
f[i+1][opt] = (f[i+1][opt] + (s64)f[i][opt]*Quick(Val[i+1]-1,Num[i+1]) % MOD) % MOD;

}
}

int main()
{
T=get();
while(T--)
{
h=get();    w=get();    m=get();    n=get();

all=(1<<n)-1;
x_num=y_num=0;
for(int i=1;i<=n;i++)
{
a[i].x1=get();  a[i].y1=get();  a[i].x2=get();  a[i].y2=get();
a[i].x1--;  a[i].y1--;
a[i].val=get();
qx[++x_num]=a[i].x1;    qx[++x_num]=a[i].x2;
qy[++y_num]=a[i].y1;    qy[++y_num]=a[i].y2;
}
qx[++x_num]=0;  qx[++x_num]=h;
qy[++y_num]=0;  qy[++y_num]=w;

Deal_first();
Deal();

printf("%d\n",f[cnt][all]);
}
}
View Code

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: