【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
23 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
2876475
HINT
T≤5, 1≤h,w,m≤10000, 1≤v≤m, 1≤n≤10Main 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
相关文章推荐
- [BZOJ5010][Fjoi2017]矩阵填数(状压DP)
- BZOJ5010: [Fjoi2017]矩阵填数
- [BZOJ5010][FJOI2017]矩阵填数(状压DP)
- 【BZOJ】5010: [Fjoi2017]矩阵填数
- bzoj5010: [Fjoi2017]矩阵填数
- bzoj 5010: [Fjoi2017]矩阵填数
- 【bzoj4818】[Sdoi2017]序列计数 矩阵乘法
- bzoj4818 [Sdoi2017]序列计数(矩阵)
- bzoj 4870: [Shoi2017]组合数问题 [矩阵乘法优化dp]
- BZOJ 1002([FJOI2007]轮状病毒-基尔霍夫矩阵)
- [bzoj1002] [FJOI2007]轮状病毒轮状病毒(基尔霍夫矩阵)
- [状压DP][概率与期望][二分图] BZOJ 5006 && LOJ #2290. 「THUWC 2017」随机二分图
- 17.7.3小结、【矩阵快速幂】2017武大校赛I题:A simple math problem即nyoj2333、 bzoj2326
- 【BZOJ 4818】 4818: [Sdoi2017]序列计数 (矩阵乘法、容斥计数)
- BZOJ 1002: [FJOI2007]轮状病毒【生成树的计数与基尔霍夫矩阵简单讲解+高精度】
- 【BZOJ 1002】 [FJOI2007]轮状病毒 【矩阵树定理】【留坑】
- BZOJ 4870 [Shoi2017]组合数问题 ——动态规划 矩阵乘法
- bzoj 1002 [FJOI2007]轮状病毒 高精度&&找规律&&基尔霍夫矩阵
- [BZOJ4861][BJOI2017]魔法咒语(AC自动机+矩阵优化DP)
- BZOJ 4818 [Sdoi2017]序列计数 ——矩阵乘法