省选模拟(12.10) T1 submat

标签: 省选 结论 容斥
114人阅读 评论(0) 收藏 举报
分类:

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;
}
查看评论

snoi省选模拟赛 day3t2 游戏

snoi省选模拟赛 day3t2 游戏
  • Rising_shit
  • Rising_shit
  • 2018-01-17 21:04:23
  • 161

省选模拟赛[SHOI2017] Day2

比赛过程17:50  cccc来晚了来晚了gggggg….. 看了一遍题, woc今年的省选??? 感觉要gg. 怎么一道题都没有见过啊, 今天又要爆零啦…. 看了一遍t1, 好长啊? 所以看完了还要...
  • MaxMercer
  • MaxMercer
  • 2017-12-18 08:31:53
  • 171

【清华集训2017模拟12.10】回文串

DescriptionNYG 很喜欢研究回文串问题,有一天他想到了这样一个问题: 给出一个字符串 S,现在有 4 种操作: • addl c :在当前字符串的左端加入字符 c; • addr c...
  • alan_cty
  • alan_cty
  • 2017-12-09 21:39:36
  • 249

ZJOI2018一试前记

三月已至。那令人振奋的省选月。1个月前PKUWC2018签废纸的阴影犹在。.失常的发挥,总令人感叹世事弄人——却不想为何次次如此。三月,终究到来,这背水一战的时刻。NOIp440,较严重的失误。520...
  • chenyanbo1111
  • chenyanbo1111
  • 2018-03-01 20:39:50
  • 697

省选模拟赛 project

solution: 最小割问题。 建如下边: (S,i,Ai)代表选用A语言编写第i个项目; (i,T,Bi)代表选用A语言编写第i个项目; 其后注意要反向连边 (i,j,D)代表选用B语...
  • keshuqi
  • keshuqi
  • 2016-07-20 21:03:25
  • 190

SDOI2014 Round2 Day2

loli说今天的题会水一点……尽力去写吧 ---------------6:40
  • u012732945
  • u012732945
  • 2014-05-18 06:38:56
  • 1079

SDOI2014 Round2 Day1

二轮了,努力就好,有了上次爆零经验这次
  • u012732945
  • u012732945
  • 2014-05-17 06:42:46
  • 879

ZJOI2018DAY1滚粗记

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Day1滚粗辣! Day 0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;n...
  • Effervescence
  • Effervescence
  • 2018-03-26 13:11:33
  • 172

SNOI省选模拟赛Round1 T2 string

题目大意:有n个01串,每个字符串可以看做是其本身的无限重复,求任意两个字符串的最长公共前缀的最长长度,保证每个串不存在周期。 n 分析:我在现场打了暴力拿了80分诶。 正解是trie树。 枚...
  • qq_39791208
  • qq_39791208
  • 2018-01-15 20:30:26
  • 96
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 2万+
    积分: 2169
    排名: 2万+
    博客专栏
    最新评论