1. 程式人生 > >BZOJ3142&&洛谷P3228 [HNOI2013]數列

BZOJ3142&&洛谷P3228 [HNOI2013]數列

毒瘤數學題

思路

我們考慮如何限定同類數列? 用漲幅相同限定 所以我們通過列舉每兩天之間的漲幅就可以枚舉出一類數列,這類數列有多少種呢?我們假設第i天到第i+1天的漲幅是s,第一天漲了s1,第二天漲s2,那麼到最後我們可以通過調大第1天的價格,那麼後面就一起變大了,所以因為有每天都不大於n這個限制,所以最大的那天應該是最後一天也就是第k天要小於等於n,不難發現將前幾天的漲幅相加再加上第一天的價格就是最後一天的價格,所以第一天的價格滿足最後一天不大於n有ni=1k1sin-\sum\limits_{i=1}^{k-1}s_i種,所以我們可以列出答案的式子 ans=

s1=1ms2=1m...sk1=1m(ni=1k1si)ans=\sum\limits_{s1=1}^{m}\sum\limits_{s2=1}^{m}...\sum\limits_{s_{k-1}=1}^{m}(n-\sum\limits_{i=1}^{k-1}s_i) 複雜度大概是O(mk)O(m^k),炸了! 看看能不能化簡 ans=nmk1s1=1ms2=1m...sk1=1mi=1k1sians=n*m^{k-1}-\sum\limits_{s1=1}^{m}\sum\limits_{s2=1}^{m}...\sum\limits_{s_{k-1}=1}^{m}\sum\limits_{i=1}^{k-1}s_i
ans=nmk1i=1k1s1=1ms2=1m...sk1=1msians=n*m^{k-1}-\sum\limits_{i=1}^{k-1}\sum\limits_{s1=1}^{m}\sum\limits_{s2=1}^{m}...\sum\limits_{s_{k-1}=1}^{m}s_i ans=nmk1(k1)mk2i=1mians=n*m^{k-1}-(k-1)*m^{k-2}\sum\limits_{i=1}^{m}i
ans=nmk1(k1)mk2m(m+1)2ans=n*m^{k-1}-(k-1)*m^{k-2}*\frac{m*(m+1)}{2} 然後就log可做啦!交給快速冪就好了

程式碼

//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lli long long int
using namespace std;
lli n,k,m,p;
inline lli fpow(lli x,lli y)
{
	lli z=1;
	for (;y;x=(x*x)%p,y>>=1)
	if (y&1) z=(z*x)%p;
	return z;
}
signed main()
{
	cin>>n>>k>>m>>p;k--;n%=p;
	lli a1=(n*fpow(m,k))%p;
	lli a2=(k*fpow(m,k-1))%p;
	lli a3=(m*(m+1)/2)%p;
	cout<<(a1-(a2*a3)%p+p)%p;
	return 0;
}