LuoguP5075 [JSOI2012]分零食

題意

\(A\)個人,\(m\)個糖,你可以選擇一個\(k\),使第\(1\)~\(k\)個人每個人至少得到一個糖,並且第\(k+1\)~\(A\)個人都得不到糖。\(m\)個糖必須給完。對於每個方案都有一個歡樂值,歡樂值=\(\prod_{i=1}^kOx_i^2+Sx_i+U\),其中\(OSU\)都是給定的係數,\(x_i\)爲第\(i\)個人拿到的糖的數量。求所有方案的歡樂值的和。


這題不用NTT啊......

有個比較naive的\(dp\):設\(f_{i,j}\)表示前\(i\)個人一共拿到了\(j\)個糖的所有方案的歡樂值之和,那麼有轉移方程:

\[ f_{i,j}=\sum_{k=1}^{j-i+1}f_{i-1,j-k}\times(Ok^2+Sk+U) \]

初始值可以設\(f_{0,0}=1\)。這個\(dp\)的複雜度就是\(O(Am^2)\)。一個優化就是,由於最多前\(m\)個人拿到糖(每個人至少拿一個糖),所以\(i\)只用枚舉到\(min(m,A)\),複雜度爲\(O(m^3)\)

觀察轉移方程的結構,可以發現這樣一個優化:

\[ f_{i,j-1}=\sum_{k=1}^{j-i}f_{i-1,j-1-k}\times(Ok^2+Sk+U)\\ =\sum_{k=2}^{j-i+1}f_{i-1,j-k}\times[O(k-1)^2+S(k-1)+U]\\ =\sum_{k=2}^{j-i+1}f_{i-1,j-k}\times(Ok^2+Sk+U)- \sum_{k=2}^{j-i+1}f_{i-1,j-k}\times(2Ok-O+S)\\ =f_{i,j}-f_{i-1,j-1}\times(O+S+U)- \sum_{k=2}^{j-i+1}f_{i-1,j-k}\times(2Ok-O+S) \]

觀察最後這個\(\sum\),設\(g_{i,j}=\sum_{k=1}^{j-i+1}f_{i-1,j-k}\times(2Ok-O+S)\);那麼求\(f\)的式子可以寫成:

\[ f_{i,j-1}= f_{i,j}-f_{i-1,j-1}\times(O+S+U)-g_{i,j}+f_{i-1,j-1}\times(O+S)\\ =f_{i,j}-Uf_{i-1,j-1}-g_{i,j} \]

那麼\(f_{i,j}=f_{i,j-1}+Uf_{i-1,j-1}+g_{i,j}\)

\(f\)的轉移變成\(O(1)\)的了。但\(g\)還是\(O(n)\)的。觀察\(g\)的結構,可以類似地寫出求\(g\)的優化:

\[ g_{i,j-1}=\sum_{k=1}^{j-i}f_{i-1,j-1-k}\times(2Ok-O+S)\\ =\sum_{k=2}^{j-i+1}f_{i-1,j-k}\times[2O(k-1)-O+S]\\ =\sum_{k=2}^{j-i+1}f_{i-1,j-k}\times(2Ok-O+S)- \sum_{k=2}^{j-i+1}f_{i-1,j-k}\times 2O\\ =g_{i,j}-f_{i-1,j-1}\times(O+S)- \sum_{k=2}^{j-i+1}f_{i-1,j-k}\times 2O \]

觀察最後這個\(\sum\),設\(h_{i,j}=\sum_{k=1}^{j-i+1}f_{i-1,j-k}\times 2O\);那麼求\(g\)的式子可以寫成:

\[ g_{i,j-1}= g_{i,j}-f_{i-1,j-1}\times(O+S)-h_{i,j}+f_{i-1,j-1}\times 2O\\ =g_{i,j}-f_{i-1,j-1}\times(S-O)-h_{i,j} \]

那麼\(g_{i,j}=g_{i,j-1}+f_{i-1,j-1}\times(S-O)+h_{i,j}\)

每個\(g\)也可以\(O(1)\)求了,而且注意到\(h\)就是前綴和,每個\(h\)也可以\(O(1)\)求,所以整個\(dp\)被優化到了\(O(m^2)\)

可以通過嗎?時間上,複雜度雖然是\(O(m^2)\)的,但實際上由於\(i\leq j\),所以只需要循環\(\frac{m\times(m+1)}{2}\)次,也就是\(5\times 10^7\)級別,是可以過的。空間上,加上滾動數組優化也能過。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define cn const
#define gc getchar()
#define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
using namespace std;
typedef cn int cint;
il int rd(){
   rg int x(0),f(1); rg char c(gc);
   while(c<'0'||'9'<c){ if(c=='-') f=-1; c=gc; }
   while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc;
   return x*f;
}
template<typename T> il void ckmin(T &x,cn T &y){ if(x>y)x=y; }
cint maxn=10010;

int A,m,mod,O,S,U,ff,gg,hh,ans;
int f[2][maxn],g[2][maxn],h[2][maxn],pv=0,nw=1;

int main(){
    m=rd(),mod=rd(),A=rd(),O=rd(),S=rd(),U=rd();
    ff=U,gg=(S-O+mod)%mod,hh=(O<<1)%mod;
    
    f[0][0]=1;
    fp(i,1,min(m,A)){
        h[nw][i-1]=g[nw][i-1]=f[nw][i-1]=0;
        fp(j,i,m){
            h[nw][j]=(h[nw][j-1]+hh*f[pv][j-1])%mod;
            g[nw][j]=(g[nw][j-1]+gg*f[pv][j-1]+h[nw][j])%mod;
            f[nw][j]=(f[nw][j-1]+ff*f[pv][j-1]+g[nw][j])%mod;
        }
        ans=(ans+f[nw][m])%mod;
        nw^=pv^=nw^=pv;
    }
    printf("%d",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章