• 【bzoj4428】[Nwerc2015]Debugging调试 数论+记忆化搜索


    题目描述

    一个 $n$ 行的代码出了bug,每行都可能会产生这个bug。你要通过输出调试,在其中加入printf来判断bug出现的位置。运行一次程序的时间为 $r$ ,加入一条printf的时间为 $p$ ,求最坏情况下调出程序的最短时间。

    输入

    输入包括一行三个整数:
    n(1≤n≤10^6),代码行的数目;
    r(1≤r≤10^9),编译和运行程序直到它崩溃的时间量;
    p(1≤p≤10^9),增加单个的printf行所花费的时间。

    输出

    输出的最坏情况使用最优策略找到崩溃行的时间。

    样例输入

    16 1 10

    样例输出

    44


    题解

    数论+记忆化搜索

    看题第一眼dp,设 $f[i]$ 表示 $i$ 行代码最坏情况下的最短时间。那么枚举添加的printf语句数,可以得出dp方程:$f[i]=f[lceilfrac i{j+1} ceil]+jp+r$ 。

    考虑优化:$lceilfrac ni ceil$ 和下取整一样最多只有 $O(sqrt n)$ 个值。方法:从大到小枚举 $i$ ,令 $last=lceilfrac n{lceilfrac ni ceil} ceil$ ,则 $last$ 就是最后一个满足 $lceilfrac nj ceil=lceilfrac ni ceil$ 的 $j$ 。由于要让方程中的 $j$ 尽量小,因此使用 $last$ 转移。下一次令 $i=last-1$ 即可。

    但是这样 $O(nsqrt n)$ 的时间复杂度还是过不了,考虑进一步优化:只有一个询问,因此无需知道大多数无用的 $f$ 值。使用记忆化搜索,这样更新的结果就只有 $f[lceilfrac ni ceil]$ 了。

    使用微积分知识可以证得时间复杂度为 $O(n^{frac 34})$ (和杜教筛证明方法相同)

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    ll f[1000010] , r , p;
    inline int cdiv(int x , int y)
    {
    	return (x + y - 1) / y;
    }
    ll solve(int n)
    {
    	if(n == 1) return 0;
    	if(f[n]) return f[n];
    	int i , j;
    	f[n] = 1ll << 62;
    	for(i = n ; i != 1 ; i = j - 1)
    		j = cdiv(n , cdiv(n , i)) , f[n] = min(f[n] , solve(cdiv(n , i)) + (j - 1) * p + r);
    	return f[n];
    }
    int main()
    {
    	int n;
    	scanf("%d%lld%lld" , &n , &r , &p);
    	printf("%lld
    " , solve(n));
    	return 0;
    }
    

     

  • 相关阅读:
    如何在SpringMVC中使用REST风格的url
    c#实现的udt
    数据库查询服务化-缓存和分页
    c#常用数据库封装再次升级
    c#数据库连接池Hikari重构升级
    c# 常用数据库封装
    聊聊数据存储查询
    c#分析SQL语句
    c# 分析SQL语句中的表操作
    c#最近博文结尾
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8135724.html
Copyright © 2020-2023  润新知