BZOJ3142 [Hnoi2013]数列

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

 

Description

小 T最近在学着买股票,他得到内部消息:F公司的股票将会疯涨。股票每天的价格已知是正整数,并且由于客观上的原因,最多只能为N。在疯涨的K天中小T观察 到:除第一天外每天的股价都比前一天高,且高出的价格(即当天的股价与前一天的股价之差)不会超过M,M为正整数。并且这些参数满足M(K- 1)<N。
小T忘记了这K天每天的具体股价了,他现在想知道这K天的股价有多少种可能

Input

只有一行用空格隔开的四个数:N、K、M、P。对P的说明参见后面“输出格式”中对P的解释。
输入保证20%的数据M,N,K,P≤20000,保证100%的数据M,K,P≤109,N≤1018 。

Output

仅包含一个数,表示这K天的股价的可能种数对于P的模值。

Sample Input

7 3 2 997

Sample Output

16
【样例解释】
输出样例的16表示输入样例的股价有16种可能:
{1,2,3},{1,2,4},{1,3,4},{1,3,5}, {2,3,4},{2,3,5},{2,4,5},{2,4,6}, {3,4,5},{3,4,6},{3,5,6},{3,5,7},{4,5,6},{4,5,7},{4,6,7},{5,6,7}

 

 

正解:组合数学

解题报告:

  这道题是一道很好的组合数学题,正好为我这种蒟蒻提供数学新思路...

  首先我们考虑如果我们按照枚举每天的股价这个思路的话,显然是行不通的,因为股价的确定除了枚举,并没有什么很好的思路。所以只能另辟蹊径。

  题目中说到了每天的增长状况,也就是在提醒我们这是一种很好的思路。考虑原数列我们用A表示,我们令ai=Ai+1-Ai,那么∑ai(1<=i<=k-1)=Ak-A1,然而A1是未知的,而且Ak<=n,那么我们可以发现A1的取值范围其实是可以得出来的,就是[1,n-∑ai]之间。那么我们考虑如果我们确定了一个a的数列,我们只能确定变化趋势,也就是说A1和a序列共同决定了这个序列的原本的样子,所以假如我们确定了a数列,可以得到的原数列有n-∑ai种。而a序列根据组合数学,一共有mk-1种可能性,所以我们可以发现整个序列一共有mk-1*(n-∑ai)种可能。

  然后我们可以发现n可以先提出来,变成mk-1*n-mk-1*∑ai,然后a序列怎么考虑呢,因为一共只可能有m种取值,所以把所有的可行的a序列(长度均为k-1)列举出来,有mk-1*(k-1)。因为每个取值的出现次数相等,所以每个数字出现了mk-2*(k-1)。然后根据等差数列提一下公因式可以得出来的。

  注意几个问题:取模的时候注意不要炸了中间值!有除法的一定要谨慎!

  具体代码如下:

 

 1 //It is made by jump~
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 using namespace std;
14 typedef long long LL;
15 const int inf = (1<<30);
16 LL n,k,m,p,ans;
17 
18 inline LL getint()
19 {
20     LL w=0,q=0; char c=getchar();
21     while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 
22     while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w;
23 }
24 
25 inline LL fastpow(LL a,LL b){
26     if(b==1) return a;   LL x=1;
27     while(b>0) {
28     if(b&1) x*=a,x%=p;
29     a*=a; a%=p;
30     b>>=1;
31     }
32     return x;
33 }
34 
35 inline void work(){
36     n=getint();  k=getint(); m=getint(); p=getint(); n%=p; m%=p;
37     LL nn=fastpow(m,k-1);
38     ans=nn;  ans*=n; ans%=p; 
39     nn=fastpow(m,k-2);
40     nn*=(k-1); nn%=p; 
41     nn*=((m+1)*m/2)%p; nn%=p;     
42     ans-=nn; ans%=p; ans+=p; ans%=p;
43     printf("%lld",ans);
44 }
45  
46 int main()
47 {
48     work();
49     return 0;
50 }
原文地址:https://www.cnblogs.com/ljh2000-jump/p/5907411.html