• UVa 12590


    题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4035

    特别恶心的分类讨论,讨论四个角的放置方法即可

    具体讨论见代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 1000010;
    const int M = 1000000007;
    
    int T, n, m, k;
    int fac[maxn], ifac[maxn];
    int f[105], g[10];
    
    int qsm(int i, int po){
    	int res = 1;
    	while(po){
    		if(po & 1) res = 1ll * res * i % M;
    		i = 1ll * i * i % M;
    		po >>= 1;
    	}
    	return res;
    }
    
    int C(int N, int K){
    	if(K < 0) return 0;
    	if(N < K) return 0; 
    	return 1ll * fac[N] * ifac[K] % M * ifac[N-K] % M;
    }
    
    ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
    
    int main(){
    	fac[0] = 1;
    	for(int i = 1 ; i <= 1000000 ; ++i) fac[i] = 1ll * fac[i-1] * i % M;
    	ifac[1000000] = qsm(fac[1000000], M - 2);
    	for(int i = 1000000 - 1 ; i >= 0 ; --i) ifac[i] = 1ll * ifac[i+1] * (i+1) % M;
    	
    	scanf("%d", &T);
    	int kase = 0;
    	while(T--){
    		int ans = 0;
    		
    		scanf("%d%d%d", &n, &m, &k);
    		
    		printf("Case %d: ", ++kase);
    		
    		if(n == 1 || m == 1){
    			printf("%d
    ", C(n*m, k));
    			continue;
    		}
    		
    		// case7 (填四个角)
    		int ans7 = 0;
    		ans7 = C(n*m-4, k-4);
    		ans = (ans + ans7) % M;
    		
    		// case6 (只填三个角) 答案乘 4
    		int ans6 = 0;
    		ans6 = C(n*m-4, k-3);
    		ans = (ans + 2ll * ans6 % M * 2 % M) % M;
    		
    		// case1 (只填两个对角) 答案乘 2  
    
    		int ans1 = 0;
    		ans1 = C(n*m-4, k-2);
    //		printf("%d
    ", ans1);
    		ans = (ans + 2ll * ans1 % M) % M;
    		
    		// case2 (只填第一行的两个角) 答案乘 2 
    		
    		int ans2 = 0;
    		
    			// case2.1 最后一行放 
    			ans2 = (ans2 + ((C(n*m-4, k-2) - C((n-1)*m-2, k-2)) % M + M) % M) % M; 
    			
    			// case2.2 最后一行不放 (容斥) 
    
    			memset(f, 0, sizeof(f)); 
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (n-1)*(m-i)) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((n-1)*(m-i)-2, k-2) % M;
    				}
    				if(i & 1){
    					ans2 = ((ans2 - f[i]) % M + M) % M;
    				} else{
    					ans2 = (ans2 + f[i]) % M;
    				}
    			}
    		ans = (ans + 2ll * ans2 % M) % M;			
    //			printf("%d
    ", ans2);
    		
    		// case3 (只填第一列的两个角) 答案乘 2 
    		
    		int ans3 = 0;
    			// case3.1 最后一列放 
    			ans3 = (ans3 + ((C(n*m-4, k-2) - C(n*(m-1)-2, k-2)) % M + M) % M) % M; 
    			
    			// case3.2 最后一列不放 
    			
    			memset(f, 0, sizeof(f));
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (n-i)*(m-1)) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((n-i)*(m-1)-2, k-2) % M;
    				}
    				if(i & 1){
    					ans3 = ((ans3 - f[i]) % M + M) % M;
    				} else{
    					ans3 = (ans3 + f[i]) % M;
    				}
    			}
    //		printf("%d
    ", ans3);
    		ans = (ans + 2ll * ans3 % M) % M;
    		
    		// case4 (只放一个角) 答案乘 4
    		
    		int ans4 = 0;
    			// case4.1 两排都放
    			ans4 = (ans4 + ((((C(n*m-4, k-1) - C((n-1)*m-2, k-1))%M - C(n*(m-1)-2, k-1))%M + C((n-1)*(m-1)-1, k-1))%M+M)%M)%M;
    			
    			// case4.2 只有最后一列放 
    			
    			int res1 = 0, res2 = 0;
    			memset(f, 0, sizeof(f)); // 随便放满足最后一行都被占领的方案数 
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (n-1)*(m-i)-1) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((n-1)*(m-i)-2, k-1) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    	
    			memset(f, 0, sizeof(f)); // 最后一列不放满足最后一行都被占领的方案数 
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (n-1)*(m-1-i)) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((n-1)*(m-1-i)-1, k-1) % M;
    				}
    				if(i & 1){
    					res2 = ((res2 - f[i]) % M + M) % M;
    				} else{
    					res2 = (res2 + f[i]) % M;
    				}
    			}
    			
    			ans4 = (ans4 + ((res1 - res2) % M + M) % M) % M;
    			
    			// case4.2 只有最后一行放 	
    			
    			res1 = 0, res2 = 0;
    			memset(f, 0, sizeof(f)); // 随便放满足最后一列都被占领的方案数 
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (m-1)*(n-i)-1) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((m-1)*(n-i)-2, k-1) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    	
    			memset(f, 0, sizeof(f)); // 最后一行不放满足最后一列都被占领的方案数 
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (m-1)*(n-1-i)) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((m-1)*(n-1-i)-1, k-1) % M;
    				}
    				if(i & 1){
    					res2 = ((res2 - f[i]) % M + M) % M;
    				} else{
    					res2 = (res2 + f[i]) % M;
    				}
    			}
    			
    			ans4 = (ans4 + ((res1 - res2) % M + M) % M) % M;
    		//	printf("%d
    ", ans4);
    		ans = (ans + 2ll * ans4 % M * 2 % M) % M;		
    		// case5 (角全都不放)
    		
    		int ans5 = 0;	
    			
    			// case5.1 两行两列都放
    			ans5 = (ans5 + (((C(n*m-4, k) - 1ll*2*C((n-1)*m-2, k)%M)%M - 1ll*2*C(n*(m-1)-2, k)%M)%M+M)%M)%M; // 随便放-至少一排不放-至少一列不放 
    			ans5 = (ans5 + ((1ll*2*C((n-1)*(m-1)-1, k)%M*2%M + C(n*(m-2), k))%M + C((n-2)*m, k))%M)%M; // +至少一行一列不放+至少两行不放+至少两列不放 
    			ans5 = (ans5 + ((-1ll*2*C((n-2)*(m-1), k)%M - 1ll*2*C((n-1)*(m-2), k)%M)%M+M)%M)%M; // -至少两行一列不放-至少一行两列不放 
    			ans5 = (ans5 + C((n-2)*(m-2), k))%M; // + 至少两行两列不放 
    			
    			// case5.2 放两行一列 答案乘 2 
    			
    			int res = 0;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // +随便放满足最后一列都被占领的方案数 
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (n-i)*(m-1)-2) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((m-1)*(n-i)-2, k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = (res + res1) % M;
    	
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // -有一行不放满足最后一列都被占领的方案数 * 2
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (n-1-i)*(m-1)-1) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((n-1-i)*(m-1)-1, k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = ((res - 2ll * res1 % M) % M + M) % M;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // -有一列不放满足最后一列都被占领的方案数
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (n-i)*(m-2)) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((n-i)*(m-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = ((res - res1)% M + M) % M;
    	
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // +两行都不放满足最后一列都被占领的方案数 
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (n-2-i)*(m-1)) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((n-2-i)*(m-1), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = (res + res1) % M;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // +一行一列都不放满足最后一列都被占领的方案数 * 2
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (n-1-i)*(m-2)) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((n-1-i)*(m-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = (res + 2ll * res1 % M) % M;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // -两行一列都不放满足最后一列都被占领的方案数
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (n-2-i)*(m-2)) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((n-2-i)*(m-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = ((res - res1) % M + M) % M;
    			
    			ans5 = (ans5 + 2ll * res % M) % M;
    			
    			// case5.3 放两列一行 答案乘 2
    			
    			res = 0;
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // +随便放满足最后一列都被占领的方案数 
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (m-i)*(n-1)-2) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((n-1)*(m-i)-2, k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = (res + res1) % M;
    	
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // -有一行不放满足最后一列都被占领的方案数 * 2
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (m-1-i)*(n-1)-1) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((m-1-i)*(n-1)-1, k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = ((res - 2ll * res1 % M) % M + M) % M;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // -有一列不放满足最后一列都被占领的方案数
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (m-i)*(n-2)) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((m-i)*(n-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = ((res - res1)% M + M) % M;
    	
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // +两行都不放满足最后一列都被占领的方案数 
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (m-2-i)*(n-1)) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((m-2-i)*(n-1), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = (res + res1) % M;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // +一行一列都不放满足最后一列都被占领的方案数 * 2
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (m-1-i)*(n-2)) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((m-1-i)*(n-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = (res + 2ll * res1 % M) % M;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // -两行一列都不放满足最后一列都被占领的方案数
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (m-2-i)*(n-2)) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((m-2-i)*(n-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = ((res - res1) % M + M) % M;
    			
    			ans5 = (ans5 + 2ll * res % M) % M;			
    			// case5.4 放两列
    			
    			res = 0;
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // +随便放满足上下两行都被占领的方案数 
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (m-i)*(n-2)) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((m-i)*(n-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = (res + res1) % M;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // -一列不放满足上下两行都被占领的方案数 * 2
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (m-1-i)*(n-2)) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((m-1-i)*(n-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = ((res - 2ll*res1%M) % M + M) % M;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // +两列都不放满足上下两行都被占领的方案数 * 2
    			for(int i = 0 ; i <= m-2 ; ++i){
    				if(k <= (m-2-i)*(n-2)) { // 能放得下 
    					f[i] = 1ll * C(m-2, i) * C((m-2-i)*(n-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = (res + res1) % M;
    			
    			ans5 = (ans5 + res) % M;
    			// case5.5 放两行 
    			
    			res = 0;
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // +随便放满足上下两行都被占领的方案数 
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (n-i)*(m-2)) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((n-i)*(m-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = (res + res1) % M;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // -一列不放满足上下两行都被占领的方案数 * 2
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (n-1-i)*(m-2)) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((n-1-i)*(m-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = ((res - 2ll*res1%M) % M + M) % M;
    			
    			res1 = 0;
    			memset(f, 0, sizeof(f)); // +两列都不放满足上下两行都被占领的方案数 * 2
    			for(int i = 0 ; i <= n-2 ; ++i){
    				if(k <= (n-2-i)*(m-2)) { // 能放得下 
    					f[i] = 1ll * C(n-2, i) * C((n-2-i)*(m-2), k) % M;
    				}
    				if(i & 1){
    					res1 = ((res1 - f[i]) % M + M) % M;
    				} else{
    					res1 = (res1 + f[i]) % M;
    				}
    			}
    			res = (res + res1) % M;
    			
    			ans5 = (ans5 + res) % M;
    		ans = (ans + ans5) % M;
    		
    		printf("%d
    ", ans);
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    任务框架--Quartz 配置文件
    地址和值
    线性基学习笔记
    S07
    如何在实际项目中使用PageHelper分页插件
    设计模式:原型模式
    常用JS代码片段
    Thomson Plaza里面的三家店以及水果大会
    13.搜索过滤
    07-多线程
  • 原文地址:https://www.cnblogs.com/tuchen/p/15116970.html
Copyright © 2020-2023  润新知