1. 程式人生 > >[BZOJ3142][Hnoi2013]數列(數學相關)

[BZOJ3142][Hnoi2013]數列(數學相關)

題目描述

傳送門

題解

題意就是給出n,k,m,p,求有多少長度為k的序列A,滿足:首項為正整數;遞增數列;相鄰兩項的差小於等於m;最大值小於等於n
設a(i)=A(i+1)-A(i),我們只考慮a(i),顯然a(i)所需要滿足的條件就是aim
一個合法的a(i)序列對答案的貢獻為
ni=1k1ai
合法的a(i)序列一共有mk1個,那麼
ans=a1=1ma2=1m...ak1=1m(na1a2...ak1)
=nmk1a1=1ma2=1m...ak1=1mi=1k1ai
從這裡可以看出,後面的一坨實際上就是1..m這些數每個數出現了(

k1)mk2次,求它們的和
所以用一下等差數列的求和公式?ans=nmk1m(m+1)2(k1)mk2

程式碼

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define LL long long

LL n,m,k,Mod,ans;

LL fast_pow(LL a,LL p)
{
    LL ans=1;
    for (;p;p>>=1
,a=a*a%Mod) if (p&1) ans=ans*a%Mod; return ans; } void exgcd(LL a,LL b,LL &x,LL &y) { if (!b) x=1LL,y=0LL; else exgcd(b,a%b,y,x),y-=a/b*x; } LL inv(LL a,LL b) { LL x=0LL,y=0LL; exgcd(a,b,x,y); x=(x%b+b)%b; return x; } int main() { scanf("%lld
%lld%lld%lld"
,&n,&k,&m,&Mod); if (k==1) {printf("%lld\n",n);return 0;} ans=n%Mod*fast_pow(m%Mod,k-1)%Mod-m*(m+1)%Mod*inv(2,Mod)%Mod*fast_pow(m%Mod,k-2)%Mod*(k-1)%Mod; ans=(ans%Mod+Mod)%Mod; printf("%lld\n",ans); }