(文章修改于2020年3月22日23点45分)
第二次做这个原题时WA了两次,才发现是有些隐蔽的数据不能通过(如32532525 99999
)为防止溢出,将所有的int型改为long long int型
时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
平平在公园里游玩时捡到了很多小球,而且每个球都不一样。平平找遍了全身只发现了3个一模一样的袋子。他打算把这些小球都装进袋子里(袋子可以为空)。他想知道他总共有多少种放法。
将N个不同的球放到3个相同的袋子里,求放球的方案总数M。
结果可能很大,我们仅要求输出M mod K的结果。
现在,平平已经统计出了N<=10的所有情况。见下表:
N | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
M | 1 | 2 | 5 | 14 | 41 | 122 | 365 | 1094 | 3281 | 9842 |
输入
两个整数N,K,N表示球的个数。
输出
输出仅包括一行,一个整数M mod K 。
样例输入 Copy
11 10000
样例输出 Copy
9525
提示
对于 40%数据,10<=N<=10,000
对于100%数据,10<=N<=1,000,000,000
对于 100%数据,K<=100,000
方法一:
题目给出了1~10的结果,那我们就充分利用这10个结果。我们让后一个数减前一个数,通过归纳猜想可以发现相邻两个数的差是3的整数次幂,而且随着n的增大,在不取模的前提下这种关系肯定成立,因此构造递推关系:n=1时,f(1)=1;n>=2时,f(n)-f(n-1)=3^(n-2)
,利用累加法求得通项为f(n)=(3^(n-1)+1)/2
。经检验通项公式符合题意。
方法二:
直接推导:假设3个盒子都不相同,则每个球都有3种选择,n个球有3^n次种,实际上需要除去重复的部分,即除以3!,而当有两个袋子为空时,只算了三种情况,需要补足,因此答案为(3^n+3)/6
,即(3^(n-1)+1)/2
。
得到通项公式后,接下来就求(3^(n-1)+1)/2
对K取模。实际操作发现,如果直接运用模的运算法则计算的话,过程较麻烦,而且答案也不对,搞了好久才发现是1/2的问题。现令(3^(n-1)+1)/2=x*k+t
,这里的t是取模后的结果。一般地,有(a/b)mod c=(a mod (bc))/b
,这样的目的是消除分式对取模的影响。转化一下就得到3^(n-1)+1=2*x*k+2*t
,t=((3^(n-1)+1)%(2*k))/2=((3^(n-1)%(2*k)+1)%(2*k))/2
,这里的1%(2*k)
直接写成1,3^(n-1)%(2*k)
部分就是典型的快速幂算法。
#include<stdio.h>
typedef long long int lli;//为防止溢出,将所有的int型改为long long int型
lli quick_pow(lli b,lli mod)//快速幂
{lli a=3;lli ans=1;mod=mod*2;while(b){if(b%2==1){ans=ans*a%mod;}a=a*a%mod;b=b/2;}return ans;
}
int main()
{lli n;lli k;scanf("%lld %lld",&n,&k);printf("%lld",(quick_pow(n-1,k)+1)%(2*k)/2);return 0;
}