zoukankan      html  css  js  c++  java
  • 没有名字 [整除分块优化dp]

    没有名字


    color{red}{正解部分}

    F[i,j]F[i, j] 表示 ii 位置填 jj 满足条件的方案数, 则 F[i,j]=k=1MjF[i1,k]F[i, j] = sumlimits_{k=1}^{lfloor frac{M}{j} floor} F[i-1, k], 直接转移复杂度 O(NM2)O(NM^2), 不可过 .

    观察到 Mjlfloor frac{M}{j} floor 的取值仅有 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]]] ]egin{aligned} & F'[i, j] \ & = (r[j]-l[j]+1) imes sumlimits_{k=1}^{val[j]} F[i-1, k] \ & = (r[j]-l[j]+1) imes sumlimits_{k=1}^{Mp[frac{M}{val[j]]}]} F'[i-1,k] \ & = (r[j]-l[j]+1) imes 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]} floor = lfloor frac{M}{frac{M}{l[j]}} floor 的值是 单调不增 的, 且只会变化 Msqrt{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
    ", g[N][cnt]);
            return 0;
    }
    
  • 相关阅读:
    Unity动态更换图片
    (特殊的)增删改查
    SQL Server 锁
    [转]排序规则
    [转]C#编写Windows服务程序图文教程
    [转]FreeTextBox使用详解 (版本3.1.1)
    [转]Newtonsoft.Json序列化和反序列
    C#性能优化实践(摘抄)
    一、PID控制原理
    POJ 2255已知二叉树前序中序求后序
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822413.html
Copyright © 2011-2022 走看看