【BZOJ5010】【FJOI2017】矩阵填数 [状压DP]
矩阵填数
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
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
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cstdio> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cmath> 8 #include<map> 9 using namespace std; 10 typedef long long s64; 11 12 const int ONE=505; 13 const int MOD=1e9+7; 14 const int INF=2147483640; 15 16 int T; 17 int h,w,m,n,all; 18 int qx[ONE],x_num,qy[ONE],y_num; 19 int Num[ONE],Val[ONE],op[ONE],cnt; 20 int f[ONE][3001]; 21 22 struct power 23 { 24 int x1,y1; 25 int x2,y2; 26 int val; 27 }a[ONE]; 28 29 int get() 30 { 31 int res=1,Q=1;char c; 32 while( (c=getchar())<48 || c>57 ) 33 if(c=='-')Q=-1; 34 res=c-48; 35 while( (c=getchar())>=48 && c<=57 ) 36 res=res*10+c-48; 37 return res*Q; 38 } 39 40 s64 Quick(s64 a,int b) 41 { 42 s64 res=1; 43 while(b) 44 { 45 if(b&1) res=res*a%MOD; 46 a=(s64)a*a%MOD; 47 b>>=1; 48 } 49 return res; 50 } 51 52 void Deal_first() 53 { 54 sort(qx+1,qx+x_num+1); x_num=unique(qx+1,qx+x_num+1)-qx-1; 55 sort(qy+1,qy+y_num+1); y_num=unique(qy+1,qy+y_num+1)-qy-1; 56 57 cnt=0; 58 for(int i=2;i<=x_num;i++) 59 for(int j=2;j<=y_num;j++) 60 { 61 int lenx=qx[i]-qx[i-1]; 62 int leny=qy[j]-qy[j-1]; 63 Num[++cnt]=lenx*leny; Val[cnt]=m; op[cnt]=0; 64 65 for(int l=1;l<=n;l++) 66 if(a[l].x1<=qx[i-1] && qx[i]<=a[l].x2 && a[l].y1<=qy[j-1] && qy[j]<=a[l].y2) 67 Val[cnt]=min(Val[cnt],a[l].val); 68 69 for(int l=1;l<=n;l++) 70 if(a[l].val==Val[cnt]) 71 if(a[l].x1<=qx[i-1] && qx[i]<=a[l].x2 && a[l].y1<=qy[j-1] && qy[j]<=a[l].y2) 72 op[cnt]|=(1<<l-1); 73 } 74 } 75 76 void Deal() 77 { 78 memset(f,0,sizeof(f)); 79 f[0][0]=1; 80 81 for(int i=0;i<=cnt-1;i++) 82 for(int opt=0;opt<=all;opt++) 83 if(f[i][opt]) 84 { 85 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; 86 f[i+1][opt] = (f[i+1][opt] + (s64)f[i][opt]*Quick(Val[i+1]-1,Num[i+1]) % MOD) % MOD; 87 88 } 89 } 90 91 int main() 92 { 93 T=get(); 94 while(T--) 95 { 96 h=get(); w=get(); m=get(); n=get(); 97 98 all=(1<<n)-1; 99 x_num=y_num=0; 100 for(int i=1;i<=n;i++) 101 { 102 a[i].x1=get(); a[i].y1=get(); a[i].x2=get(); a[i].y2=get(); 103 a[i].x1--; a[i].y1--; 104 a[i].val=get(); 105 qx[++x_num]=a[i].x1; qx[++x_num]=a[i].x2; 106 qy[++y_num]=a[i].y1; qy[++y_num]=a[i].y2; 107 } 108 qx[++x_num]=0; qx[++x_num]=h; 109 qy[++y_num]=0; qy[++y_num]=w; 110 111 Deal_first(); 112 Deal(); 113 114 printf("%d\n",f[cnt][all]); 115 } 116 }