没有名字 [整除分块优化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;
}
posted @ 2019-10-24 20:41  XXX_Zbr  阅读(131)  评论(0编辑  收藏  举报