沒有名字 [整除分塊優化dp]

沒有名字


\color{red}{正解部分}

F[i,j]F[i, j] 表示 ii 位置填 jj 滿足條件的方案數, 則 F[i,j]=k=1MjF[i1,k]F[i, j] = \sum\limits_{k=1}^{\lfloor \frac{M}{j} \rfloor} F[i-1, k], 直接轉移複雜度 O(NM2)O(NM^2), 不可過 .

觀察到 Mj\lfloor \frac{M}{j} \rfloor 的取值僅有 O(M)O(\sqrt{M}) 個, 且轉移是前綴和的形式, 因此考慮使用 整除分塊前綴和 優化,

F[i,j]F'[i, j] 表示 ii 位置填 整除分塊 從大到小jj 值所對應的 分母 的方案數, g[i,j]g[i, j] 表示 對應 前綴和, 則

    F[i,j]=(r[j]l[j]+1)×k=1val[j]F[i1,k]=(r[j]l[j]+1)×k=1Mp[Mval[j]]]F[i1,k]=(r[j]l[j]+1)×g[i1,Mp[Mval[j]]] ]\begin{aligned} &\ \ \ \ F'[i, j] \\ & = (r[j]-l[j]+1)\times \sum\limits_{k=1}^{val[j]} F[i-1, k] \\ & = (r[j]-l[j]+1) \times \sum\limits_{k=1}^{Mp[\frac{M}{val[j]]}]} F'[i-1,k] \\ & = (r[j]-l[j]+1)\times g[i-1, Mp[\frac{M}{val[j]]}]\ ]\end{aligned}

最後 ans=g[N,cnt]ans = g[N, cnt], 使用 std::map<int, int> 時間複雜度 O(NMlogM)O(N \sqrt{M} \log M) .

cntcnt 爲取值的總個數, 整除分塊的值從前往後單調不增 : M M-1 M-1 M-2 M-2 M-2 …
Mp[x]Mp[x]xx 對應的塊的編號 .
val[i]val[i] 表示編號爲 ii 的塊對應的值 .

但是 Mp[x]Mp[x] 直接使用 std::map<int,int> 儲存會 TLETLE,
觀察到在 從小到大 枚舉 jj 時, Mval[j]=MMl[j]\lfloor \frac{M}{val[j]} \rfloor = \lfloor \frac{M}{\frac{M}{l[j]}} \rfloor 的值是 單調不增 的, 且只會變化 M\sqrt{M} 次, 因此可以使用指針維護 .

時間複雜度 O(NM)O(N \sqrt{M}) .


\color{red}{實現部分}

#include<bits/stdc++.h>
#define reg register

const int maxn = 1000005;
const int mod = 1e9 + 7;

int N;
int M;
int cnt;

int Mp[maxn];
int val[maxn];
int llim[maxn];
int rlim[maxn];
int F[102][maxn];
int g[102][maxn];

int main(){
        scanf("%d%d", &N, &M);
        for(reg int l = 1, r; l <= M; l = r+1){
                r = M/(M/l), llim[++ cnt] = l, rlim[cnt] = r;
                F[1][cnt] = r-l+1, val[cnt] = M/l;
                g[1][cnt] = (g[1][cnt-1] + F[1][cnt]) % mod;
        }
        for(reg int i = 2; i <= N; i ++){
                int t = cnt;
                for(reg int j = 1; j <= cnt; j ++){
                        while(t >= 1 && M/(M/llim[j]) > val[t]) t --;
                        F[i][j] = (rlim[j]-llim[j]+1)*1ll*g[i-1][t] % mod;
                        g[i][j] = (g[i][j-1] + F[i][j]) % mod;
                }
        }
        printf("%d\n", g[N][cnt]);
        return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章