这题好没意思啊,怀疑拉不开区分度。
题意:求一个递增序列,每两个相邻数字之间的差值不超过m,最后一个值不能大于n。
分析:网上好多人用了差分,我没想到。然后YY了一发生成函数。
考虑构造生成函数G(x) = x+x2+...+xm.
我们的目标是求这个G(k-1)(x)的很多个前缀和。
具体来说是求什么呢?
这题的重点其实在于一个注意不到的细节:(k-1)*m<n。
这意味着,当第一项为1时,所有的答案一定被满足。
也就是说,当第一项为1时,对应的答案是G(k-1)(x)的所有项数之和。
实际上通过举例子,你会发现这样一个性质:[xi+k-1]G(k-1)(x) = [x(k-1)*m-i]G(k-1)(x) 其中i的范围自己想一下。
这个性质怎么证明呢:归纳
考虑将Gi(x)除以xi,然后乘以G(x)/x,然后把除的东西乘上。由于上一个具有这样的对称性,画图之后我们发现乘的时候是对称的,那么这个性质仍然满足。
通过我们对高斯的了解,我们知道他10岁的时候想到了1加到100的方法,他非常聪明,而我们只能照葫芦画瓢。
什么意思呢,我们考虑第一项为i的时候,在某个位置的时候出现了第一个不满足的序列。
然后从后往前,在某个位置出现了第一个满足的序列。
把两个匹配在一起,这样子我们得到了一个完整的序列,即(m)^(k-1).
众所周知,高斯是个很聪明的孩子,那么他用的方法很可能是将1到100翻倍然后两两配对,最后除以2。这个方法在这不好用。
我们不容易确认2在这个模数下有逆。
取而代之的方法是用类似1配99,2配98的方法。考虑多了一项怎么办。
不难发现的是,多出来的一项一定是配出来的结果的一半,不说明了。
做这个答案的时候一定要先留下一个m,将这个m除以2再乘进去。
代码:
#include<bits/stdc++.h>
using namespace std; typedef long long ll; ll n,k,m,mod;
ll pw,maxx; ll fast_pow(int now,int p){
if(p == ) return ;
if(p == ) return now;
ll z = fast_pow(now,p/);
z *= z; z %= mod;
if(p & ) z*=now,z%=mod;
return z;
} int main(){
scanf("%lld%lld%lld%lld",&n,&k,&m,&mod);
maxx = (k-)*m;
if(k == ){printf("%lld",n);return;}
pw = fast_pow(m,k-);
ll fir = n-maxx+; // diyige youxiaci de
ll lst = n-(k-); // zuihouyige youxiacide
ll len = lst-fir+,multi = len/;
ll ans = ((multi+n-maxx)%mod)*pw; ans %= mod;
if(len & ){
pw = fast_pow(m,k-);
pw *= (m/);pw %= mod;
ans += pw;ans %= mod;\
}
printf("%lld",ans);
return ;
}