省选模拟(12.10) T1 submat

SUBMAT

题目背景:

12.10 省选模拟T1

分析:容斥 + 结论

 

直接考虑H = W = 4,会出现什么情况,显然,矩阵中存在很多的等量关系,也就是方程,我们大概要做的就是求解方程组的解的个数。考虑到:

由此可以得到:


注意到,对于一个x, y <= 4,必然有,



所以,我们可以通过枚举H * W中的哪些格子是向下相等的,哪些格子是向右相等的,显然,向下的和向右的是独立的,那么我们可以通过枚举有多少个1,来直接进行组合数,但是注意到,如果向下相等的,同样满足了向右相等,就会重复,所以需要推导容斥的公式,具体详见代码注释。复杂度O(2HW(H + W)2)

PS:代码中的表格表示枚举容斥当中固定多少位置,获得对应和的方案数。

 

Source:

/*
	created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <map>
#include <bitset>

inline char read() {
	static const int IN_LEN = 1024 * 1024;
	static char buf[IN_LEN], *s, *t;
	if (s == t) {
		t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
		if (s == t) return -1;
	}
	return *s++;
}

///*
template<class T>
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = read(), iosig = false; !isdigit(c); c = read()) {
		if (c == -1) return ;
		if (c == '-') iosig = true;	
	}
	for (x = 0; isdigit(c); c = read()) 
		x = ((x << 2) + x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
//*/

const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(char c) {
	if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
	*oh++ = c;
}


template<class T>
inline void W(T x) {
	static int buf[30], cnt;
	if (x == 0) write_char('0');
	else {
		if (x < 0) write_char('-'), x = -x;
		for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
		while (cnt) write_char(buf[cnt--]);
	}
}

inline void flush() {
	fwrite(obuf, 1, oh - obuf, stdout);
}

/*
template<class T>
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
		if (c == '-') iosig = true;	
	for (x = 0; isdigit(c); c = getchar()) 
		x = ((x << 2) + x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
//*/

const int mod = 1000000000 + 7;
const int MAXN = 10;

int n, m, h, w;
int c[MAXN][MAXN], row[MAXN], col[MAXN];

inline int mod_pow(int a, int b) {
	int ans = 1;
	for (; b; b >>= 1, a = (long long)a * a % mod)
		if (b & 1) ans = (long long)ans * a % mod;
	return ans;
}

inline void solve() {
	long long ans = 0;
	R(n), R(m), R(h), R(w);
	for (int i = 0; i <= 4; ++i) c[i][i] = c[i][0] = 1;
	for (int i = 2; i <= 4; ++i)
		for (int j = 1; j < i; ++j)
			c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
		
	for (int cur = 0, s = (1 << h * w); cur < s; ++cur) {
		for (int i = 0; i < 4; ++i) row[i] = col[i] = 0;
		long long ret = 1;
		for (int i = 0; i < h * w; ++i) 
			if (cur & (1 << i)) 
				row[i / w]++, col[i % w]++;
		/*1表示向下相等, 显然相邻两列的和相等,固定了col[i]个数,其他的数满足
		和相等即可,直接枚举和,和方案数乘法原理即可,j为枚举和,z表示一共有z列*/
		for (int i = 0; i < w; ++i) {
			int z = (m - 1 - i) / w + 1, x = 0;
			for (int j = 0; j <= col[i]; ++j) 
				x = (x + mod_pow(c[col[i]][j], z)) % mod;
			ret = ret * x % mod;
		}
		for (int i = 0; i < h; ++i) row[i] = w - row[i]; // 每一行有多少向右相等 
		for (int i = 0; i < h; ++i) {
			int z = (n - 1 - i) / h + 1, x = 0;
			switch (row[i]) {
				case 0: x = 1;
				// 四个都向下相等,情况相同 
						break ;
				case 1: x = 0;
				//显然只有这一个数,那么肯定满足向下和向右 
						break ;
				//以下三张表格,表示枚举容斥当中枚举固定多少位置,
				//获得的对应和的方案数 
				case 2: x = mod_pow(2, z) - 2;
						// 和/固定个数 0     1    2      
						//  0          1     2    1          
						//  1          2^z   4    2        
						//  2          1     2    1     
						break ;
				case 3: x = mod_pow(3, z) * 2 % mod - 
						(long long)mod_pow(2, z) * 6 % mod + 6;
						(x < 0) ? (x += mod) : (0);
						break ;
						// 和/固定个数 0     1        2     3 
						//  0          1     3        3     1    
						//  1          3^z   3*2^z+3  9     3   
						//  2          3^z   3*2^z+3  9     3   
						//  3          1     3        3     1   
				case 4: x = (long long)mod_pow(6, z) + 2LL * mod_pow(4, z) % mod
						  - 16LL * mod_pow(3, z) % mod
						  + 24LL * mod_pow(2, z) % mod - 14;
						x %= mod, (x < 0) ? (x += mod) : (0);
						// 和/固定个数 0     1        2          3    4 
						//  0          1     4        6          4    1
						//  1          4^z   4*3^z+4  6*2^z+12   16   4
						//  2          6^z   8*3^z    12*2^z+12  24   6
						//  3          4^z   4*3^z+4  6*2^z+12   16   4
						//  4          1     4        6          4    1
						break ;
			}
			ret = ret * x % mod;
		}
		ans = (ans + ret) % mod;
	}
	std::cout << ans;
}


int main() {
	freopen("submat.in", "r", stdin);
	freopen("submat.out", "w", stdout);
	solve();
	return 0;
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/scar_lyw/article/details/78874266
文章标签: 省选 结论 容斥
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭