HDU4704Sum 费马小定理+大数取模

题目链接

  hdu4704

题目大意

给定一个数n 将其分解,Si 表示将n拆成i个数的方案数

求sum( si ) 1<=i<=n;


分析:

隔板原理, n个木棍,n-1个缝,

分成1份则是C(n-1,0);

分成2份则是C(n-1,1);

分成3份则是C(n-1,2);

...

分成n份则是C(n-1,n-1);

ans = sum( C(n-1,i) ) (0<=i<=n-1)

=2^(n-1);


题目分析:

  因为n实在是太大太大了,这可咋办啊?!n<10100000

  做这场的时候没有注意到,也是当时没有看过什么是费马小定理,居然跟模值有关系!mod=1000000007。这个mod有什么特点呢?它是个质数。

  费马小定理揭示了:当p是一个素数并且a和p互质时,ap-1 %p≡1。

  那么在这道题里a=2,p=mod。显然满足费马小定理。再根据同余模定理

  当n>p时:设m=n-p。那么an-1=am+p-1=ap-1*am

因而an-1%p=am+p-1%p=((ap-1%p )*(am%p))%p=1*am%p。

  这么我们就可以断定mod-1是一个循环节。先用大数对它取模,然后数据量就可以承受得起了,再用快速幂,这道题就解了!

ps:这里解释下大数取模

数学上,两个整数除以同一个整数,若得相同余数,则二整数同余(英文:Modular arithmetic;德文:Kongruenz)。同余理论常被用于数论中。最先引用同余的概念与符号者为德国数学家高斯。——摘自 百度百科


(a + b)% n = ((a % n) + (b % n)) % n

(a - b) % n = ((a % n) - (b % n) + n) % n

a * b % n = (a % n) * (b % n) % n


下面是对大整数取模,边乘边取余。证明就用上面的定理。

用字符数组存大整数str

int ans = 0;

for(int i =0; i < str.length(); ++i)

ans = (ans * 10 + str[i] - '0') % MOD;


#include <cstdio>
#include <cstring>
#include <algorithm>
#include<iostream>
using namespace std;
#define md 1000000007
#define maxn 100005
char s[maxn];
long long power(long long a,long long b)
{
	long long base=a,sum=1;
	while(b!=0)
	{
		if(b&1)
		sum=(sum*base)%md;
		base=(base*base)%md;
		b>>=1;
	}
	return sum%md;
}
int main()
{
	while(~scanf("%s",s))
	{
		int len=strlen(s);
		long long num=0;
		for(int i=0;i<len;i++)//大数取模 同余模定理
		{
			num=(num*10+(int)(s[i]-'0'))%(md-1);
		}
		//因为计算2^(n-1),相当于计算 2^(num-1)
		if(num==0)//说明num=md-1
		{
			num=md-1;
		}
		printf("%lld\n",power(2,num-1));
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值