[题解] BZOJ 3142 [HNOI2013]数列

题目描述 Description
小T最近在学着买股票,他得到内部消息:F公司的股票将会疯涨。
股票每天的价格已知是正整数,并且由于客观上的原因,最多只能为 N N 。在疯涨的K天中小T观察到:除第一天外每天的股价都比前一天高,且高出的价格(即当天的股价与前一天的股价之差)不会超过 M M ,M为正整数。并且这些参数满足 M(K1)<N M ∗ ( K − 1 ) < N
小T忘记了这 K K 天每天的具体股价了,他现在想知道这K天的股价有多少种可能。
输入描述 Input Description
输入文件只有一行用空格隔开的四个数: N,K,M,P N , K , M , P
输出描述 Output Description
仅包含一个数,表示这 K 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}
数据范围及提示 Data Size & Hint
20%的数据M,N,K,P20000
100%的数据 K,M,P109,N1018 K , M , P ≤ 10 9 , N ≤ 10 18

Solution

将某一个确定的上涨序列 a[1],a[2],a[3],...,a[k] a [ 1 ] , a [ 2 ] , a [ 3 ] , . . . , a [ k ] 写出来
这个序列对于总数的贡献为1,当然,是当 a[k]n a [ k ] ≤ n 的时候
显然的,维持每天上涨的价格不变,由于 a[1] a [ 1 ] 能够有多种取值,那么它就会有很多贡献,当然,变化后的 a[1] a [ 1 ] 仍然要保证 a[k]n a [ k ] ≤ n
那么能不能考虑维护一个股票价格的差分数列?就不用考虑 a[1] a [ 1 ] 的取值
并且,这个差分数列 s[1],s[2],s[3],...,s[k1] s [ 1 ] , s [ 2 ] , s [ 3 ] , . . . , s [ k − 1 ] 所做出的贡献就为 nk1i=1s[i] n − ∑ i = 1 k − 1 s [ i ]
一共有 mk1 m k − 1 个不同的差分数列,每个数列做出的贡献值为 nk1i=1s[i] n − ∑ i = 1 k − 1 s [ i ]
那么总贡献就为
mk1d=1(nk1i=1s[d][i]) ∑ d = 1 m k − 1 ( n − ∑ i = 1 k − 1 s [ d ] [ i ] )
n n 提出可得
nmk1d=1mk1i=1k1s[d][i]
现在要做的就是处理后面那一堆东西

注意, s s 显然是将所有可能的排列情况都算了进去,并且s[d][i][1,m]
那么后面一共就会有 mk1(k1) m k − 1 ∗ ( k − 1 ) 个数,并且在 [1,m] [ 1 , m ] 中完全平均分布
所以 [1,m] [ 1 , m ] 中的每个数都会出现 mk1(k1)m=mk2(k1) m k − 1 ∗ ( k − 1 ) m = m k − 2 ∗ ( k − 1 )
运用小学数学知识,将其总和化为 mk2(k1)(m+1)m2 m k − 2 ∗ ( k − 1 ) ∗ ( m + 1 ) ∗ m 2
这样就很好求解了

最终答案为 nmk1mk2(k1)(m+1)m2 n ∗ m k − 1 − m k − 2 ∗ ( k − 1 ) ∗ ( m + 1 ) ∗ m 2
快速幂就好啦╮(╯_╰)╭

努力追赶dalao中
给予我力量吧(丢脸ing

代码如下

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n,k,m,p,ans;
ll read() {
    ll ans=0,flag=1;
    char ch=getchar();
    while((ch>'9' || ch<'0') && ch!='-') ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
ll qpow(ll a,ll b,ll mod) {
    ll ans=1;
    while(b>0) {
        if(b&1) {ans*=a;ans%=mod;}
        b>>=1;a*=a;a%=mod;
    }
    return ans;
}
ll exgcd(ll a,ll b,ll &x,ll &y) {
    if(b==0) {x=1;y=0;return a;}
    ll gcd=exgcd(b,a%b,x,y);
    ll t=x;
    x=y; y=t-(a/b)*y;
}
int main() {
    n=read(),k=read(),m=read(),p=read();
    ll x,y,gcd;
    gcd=exgcd(2,p,x,y);
    x=(x%p+p)%p;
    ans+=(qpow(m,k-1,p)*(n%p))%p;
    ans-=((((qpow(m,k-1,p)*(k-1))%p*(m+1))%p)%p*x%p);
    ans=(ans%p+p)%p;
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值