bzoj5010:[FJOI2017]矩阵填数

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 ( 1≤x1≤x2≤h, 1≤y1≤y2≤w)
T≤5,1≤h,w,m≤10000,1≤v≤m,1≤n≤10

 

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

Source

By 佚名上传

 

题目地址:http://www.lydsy.com/JudgeOnline/problem.php?id=5010

 

题解:

今年省选一试的题目。因为我只参加过二试,所以我知道这是DAY1的。

因为告诉你的是最大值v,说明那个矩阵里面肯定要有一个数=v,这样子很不好做,我们考虑容斥,转换为每个矩阵的数都<=v,通过2^n枚举每个最大值要不要-1(用来容斥)

接下来再用2^n暴力算出其中x个的交的区域大小为多少,容斥出来,就变成了一些块,把每块的v^siz乘起来就好了。

复杂度O(T*2^(2*N))

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <iostream>

using namespace std;

const int mod = 1e9+7;

int fast_pow(int a, int x) {
	int res = 1;
	for(; x; x >>= 1) {
		if(x&1) res = 1ll*res*a%mod;
		a = 1ll*a*a%mod; 
	}
	return res;
}

int h, w, m, n;

struct info {
	int x1, y1, x2, y2, v;
} a[15], t[15], beg;

int siz(info x) { return (x.x2-x.x1+1)*(x.y2-x.y1+1); }

bool check(info x) { return x.x1 <= x.x2 && x.y1 <= x.y2 && x.v > 0; }

info add(info a, info b) {
	int x1 = max(a.x1, b.x1);
	int x2 = min(a.x2, b.x2);
	int y1 = max(a.y1, b.y1);
	int y2 = min(a.y2, b.y2);
	int v = min(a.v, b.v);
	return (info) {x1, y1, x2, y2, v};
}

int g[1<<15], f[1<<15];

void dfs1(int dep, info now, int s) {
	if(!check(now)) return;
	if(dep > n) { g[s] = siz(now); return; }
	dfs1(dep+1, now, s<<1);
	dfs1(dep+1, add(now, a[dep]), s<<1|1);
}

#define cnt __builtin_popcount

void getf() {
	dfs1(1, beg, 0);
	for(int s1 = 0; s1 < (1<<n); ++s1) 
		for(int s2 = 0; s2 <= s1; ++s2)
			if((s1&s2) == s2) {
				if(cnt(s1^s2)&1) f[s2] -= g[s1];
				else f[s2] += g[s1];
			}
}

int dfs2(int dep, int v, int s) {
	if(!v) return 0;
	if(dep > n)
		return fast_pow(v, f[s]);
	int res1 = dfs2(dep+1, v, s<<1);
	int res2 = dfs2(dep+1, min(v, t[dep].v), s<<1|1);
	return 1ll*res1*res2%mod;
}

int ans;

void dfs3(int dep, int k) {
	if(dep > n) {
		if(k) {
			int res = dfs2(1, m, 0);// printf("res = %d\n", res);
			ans += res, ans >= mod ? ans -= mod : 1;
		}
		else {
			int res = dfs2(1, m, 0);// printf("res = %d\n", -res);
			ans -= res, ans < 0 ? ans += mod : 1;
		}
		return;
	}
	t[dep] = a[dep]; dfs3(dep+1, k);
	t[dep] = a[dep]; t[dep].v--; dfs3(dep+1, k^1);
}

void work() {
	scanf("%d%d%d%d", &h, &w, &m, &n);
	memset(f, 0, sizeof f);
	memset(g, 0, sizeof g);
	beg.x1 = beg.y1 = 1, beg.x2 = h, beg.y2 = w, beg.v = w;
	for(int i = 1; i <= n; i++) 
		scanf("%d%d%d%d%d", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2, &a[i].v);
	getf(); ans = 0;
	//printf("%d\n", g[1]);
	dfs3(1, 1);
	printf("%d\n", ans);
}

int main() {
	int test;
	scanf("%d", &test);
	while(test--) work();
	return 0;
}

  

posted @ 2017-11-22 19:20  wazwaztime  阅读(352)  评论(0编辑  收藏  举报