青橙OJ A1357 矮人砍树 组合 + 快速幂取模

题目:http://www.tsinsen.com/A1357

题意:N棵树砍掉K棵,任意两棵相邻的树至少保留一棵,求方案数,对10007取余 (3<=K<=N<=500)

思路:组合数学问题,n棵树砍k棵,留下(n-k)棵树,就是(n-k)个点之间共有(n-k+1)个(包括两端)空位置,每个空位置都可以插入0或1棵被砍的树,故问题转化为求C(n-k+1,k)

分析可知k<=(n+1)/2

举例6棵树砍3棵

0/1 0/1 0/1 0/1
    

直接做求组合数,因为N最大有500,结果非常大,不能乘完后取余

此种解法麻烦的是对10007取余

参考 http://blog.csdn.net/operator456/article/details/38469799

用到

同余定理

     (a*b)%P = (a%P * b%P)%P
     (a/b)%P = (a%P * 1/b%P)%P

此题中有除法,用第二个等式

费马小定理

变形后得到:

快速幂取模(a*b%P):把b拆分成2进制表示从右向左一位一位计算,当第n项为1时令前n项的结果乘以a^(2^(n-1)),并实时取余。

#include <stdio.h>
#define MOD 10007
// 快速幂取模
long long exp_mod(int a){
	int b = MOD - 2;
	long long t = 1;
	while (b){
		if (b & 1)
			t = (t*a) % MOD;
		a = (a*a) % MOD;
		b >>= 1;
	}
	return t;
}
// 求组合数
long long zuhe(int a, int b)
{
	long long m = 1, n = 1;
	long long t = 1;
	for (int i = 0; i < b; i++)
	{
		m = (a - i);
		n = (b - i);
		t = (t * (m % MOD) * exp_mod(n)) % MOD;
	}
	return t; 
}

int main()
{
	int n, k;
	while (scanf("%d%d", &n, &k) != EOF)
	{
		printf("%d\n", zuhe(n - k + 1, k) % MOD);
	}
	return 0;
}

以后遇到排列组合问题,数字太大取模时可以用此解法

---------------------------------------------------------------------------------------------
此题还有其他解法,我一开始用组合问题分析的,结果算结果的时候取余没有好方法,故放弃,又通过画表分析,得出一个规律,可谓投机取巧。
一直不甘心,在几天之后浏览卡特兰数的解法的时候,看到上文中引用的文章,顿时找到了思路,用 同余定理+费马小定理变形+快速幂取模 解出。很有成就感,特别感谢引用文章的作者。
另外贴出我找规律的做法 
n\k123456
110
0000
2200000
3310000
4430000
5561000
66104000
上文中说到k<=(n+1)/2
data[i][j]=data[i-1][j]+data[i-2][j-1]
其实也是运用C(n,c)
#include <stdio.h>
#include <string.h>
short data[501][501];
int main()
{
	int n, k;
	while (scanf("%d%d", &n, &k) != EOF)
	{
		memset(data, 0, sizeof(data));
		for (int i = 1; i <= 500; i++)
		{
			data[i][1] = i;
			for (int j = 2; j <= (i + 1) / 2; j++)
			{
				data[i][j] = (data[i - 1][j] + data[i - 2][j - 1]) % 10007;
			}
		}
		printf("%d\n", data[n][k]);
	}
	return 0;
}

---------------------------------------------------------------------------------------------
此题还可以用动态规划,本人目前正在学习动态规划,对于本题暂时没想出来,稍后会补充。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值