1. 程式人生 > 實用技巧 >BZOJ-3142 [Hnoi2013]數列(差分+計數)

BZOJ-3142 [Hnoi2013]數列(差分+計數)

題目描述

  有一個長為 \(k\) 的嚴格單調遞增序列,最大值為 \(n\),相鄰兩數之差不超過 \(m\)(即 \(a_i-a_{i-1}\leq m\)),求滿足條件的方案數,答案對 \(p\) 取模。

  資料範圍:\(n\leq 10^{18},m,k,p\leq 10^9\)

分析

  序列第一個數 \(a_1\) 的取值難以確定,考慮把序列差分一下(\(c_i=a_i-a_{i-1}(2\leq i\leq n)\)),得到差分序列:\(c_1,c_2,\cdots,c_{k-1}\),合法序列條件為 \(\forall i\in[1,k-1],1\leq c_i\leq m\)

  設 \(f(c)\) 為差分序列 \(c\) 對答案的貢獻,對於同一個差分序列 \(c\),它對答案的貢獻為 \(n-\displaystyle\sum_{i=1}^{k-1}c_i\),即:

\[f(c)=n-\displaystyle\sum_{i=1}^{k-1}c_i \]

  由於合法序列條件為 \(\forall i\in[1,k-1],1\leq c_i\leq m\),所以一共有 \(m^{k-1}\) 種差分序列。則答案為:

\[\sum_{i=1}^{m^{k-1}}f(c)=\sum_{i=1}^{m^{k-1}}(n-\sum_{j=1}^{k-1}c_j)=n\times m^{k-1}-\sum_{i=1}^{m^{k-1}}\sum_{j=1}^{k-1}c_j \]

  後面的式子代表序列 \(c=[c_1,c_2,\cdots,c_{k-1}](1\leq c_i\leq m)\) 的所有排列之和,一共有 \(m^{k-1}·(k-1)\) 個數字。\(1\) ~ \(m\) 中的每個數都在所有排列中出現了恰好 \(\frac{m^{k-1}·(k-1)}{m}=m^{k-2}·(k-1)\) 次,總和即為 \(m^{k-2}·(k-1)·\frac{m(m+1)}{2}\)

  因此答案為:

\[n\times m^{k-1}-m^{k-2}·(k-1)·\frac{m(m+1)}{2} \]

程式碼

#include<bits/stdc++.h>
using namespace std;
long long n,m,k,p;
long long quick_pow(long long a,long long b,long long p)
{
    long long ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans%p;
}
int main()
{
    cin>>n>>k>>m>>p;
    n=n%p;m=m%p;
    long long ans1=quick_pow(m,k-1,p);
    ans1=ans1*n%p;
    long long ans2=m*(m+1)/2%p*quick_pow(m,k-2,p)%p*(k-1)%p;
    cout<<((ans1-ans2)%p+p)%p<<endl;
    return 0;
}