【BZOJ4428】[Nwerc2015]Debugging调试
Description
你看中的调试器将不会在这件事上帮助你。有代码可以通过多种方式在调试与正式发布的间隙发生不同的行为,当出现这种情况,我们可能不得不求助于更原始的调试方式。
所以,你和你的printf现在在寻求一行导致该发布版本崩溃的代码。幸运的是:增加printf语句到程序里,既不会制造bug(仍然崩溃在同一原始的代码行),也没有影响执行时间(至少不显著)。 因此,即使在每行前加一个printf语句,运行程序,直到它崩溃,并检查最后打印行。
然而,它需要一些时间来添加每个printf语句到代码,并且该程序可以具有很多行。
因此,把一个printf语句在程序的中间或许是一个更好的计划,让它运行,观察它是否在加入行前崩溃,然后继续在代码的前一或后一半寻找。
不过话又说回来,运行程序可能需要很多时间,所以时效最优的策略可能是介于两者之间。
编写计算最坏情况下的最小时间来寻找崩溃行(无论它在哪里),认为你选择最优的加printf语句策略。
我们在5小时内发布新的版本,所以这个问题十分严重,需要尽快解决。
Input
输入包括一行三个整数:
n(1≤n≤10^6),代码行的数目;
r(1≤r≤10^9),编译和运行程序直到它崩溃的时间量;
p(1≤p≤10^9),增加单个的printf行所花费的时间。
您已经运行一次程序,因此已经知道它崩溃的地方。
Output
输出的最坏情况使用最优策略找到崩溃行的时间。
Sample Input
Sample Input 1
1 100 20
Sample Input 2
10 10 1
Sample Input 3
16 1 10
1 100 20
Sample Input 2
10 10 1
Sample Input 3
16 1 10
Sample Output
Sample Output 1
0
Sample Output 2
19
Sample Output 3
44
0
Sample Output 2
19
Sample Output 3
44
题解:设f[n]表示在n行中确定崩溃位置的最小时间。我们可以枚举这一次分成多少块,即$f[n]=min{r+p imes(i-1)+f[lceilfrac n i ceil]}$,因为最坏情况一定是bug出现在分出来的最大的那块中。
发现$lceilfrac n i ceil$最多$sqrt n$个?采用记忆化搜索即可!时间复杂度?跟杜教筛差不多吧~
细节:从大到小枚举i,$last=lceil frac n {lceil frac n i ceil} ceil$。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; int n; ll A,B; ll f[1000010]; inline int Div(int a,int b) {return (a+b-1)/b;} ll DP(int x) { if(f[x]!=0x3f3f3f3f3f3f3f3fll) return f[x]; for(int i=x,last;i>=2;i=last-1) { last=Div(x,Div(x,i)); f[x]=min(f[x],A+(last-1)*B+DP(Div(x,last))); } return f[x]; } int main() { scanf("%d%lld%lld",&n,&A,&B); memset(f,0x3f,sizeof(f)); f[1]=0; printf("%lld",DP(n)); return 0; }