SCUT - 79 - 博丽灵梦与DDOS攻击 - 常微分方程
题意: DDOS 攻击的总流量值为 \(L\),寓所服务器的吞吐量为 \(d\)。
博丽灵梦可以多次使用魔法消耗 DDOS 攻击的流量:
设当前剩余攻击流量为 \(l\) ,那么灵梦可以使用 \(l+d\) 的魔法值,将剩余攻击流量变为一个随机的浮点数 \(l' (0 \leq l' < l)\) 。
服务器具有吞吐量 \(d\) ,故一旦剩余攻击流量 \(l \le d\) 时,服务器会立即消耗掉所有攻击流量。
求对于当前流量 \(L\) ,灵梦需要使用的总魔法值的期望。
设当前攻击流量为 \(x(x>d)\) 时,消耗的魔法能力期望为 \(f(x)\) ,有
\[f(x)=x+d+\frac{1}{x}\int_0^xf(t)dt
\]
两边乘 \(x\)
\[xf(x)=x^2+dx+\int_0^xf(t)dt
\]
两边微分
\[f(x)+xf'(x)=2x+d+f(x)
\]
即
\[f'(x)=2+\frac{d}{x}
\]
两边积分
\[f(x)=2x+d \ln x +C
\]
代入特殊值
\[\lim_{x \to {d^+}f(x)=2d
\]
解得
\[C=-d \ln d
\]
即
\[ f(x)=\left\{
\begin{array}{rcl}
0 && {0 \le x \le d} \\
2x+d \ln x -d \ln d && {x > d} \\
\end{array}
\right.
\]
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int T;
while(~scanf("%d", &T)) {
while(T--) {
double x, d;
scanf("%lf%lf", &x, &d);
if(x > d)
printf("%.2f\n", 2.0 * x + d * log(x) - d * log(d));
else
printf("0.00\n");
}
}
}
因为精度要求不高,也可以转为整数模拟积分:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int T = 500000;
double dp[5 * T + 100];
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int u;
scanf("%d", &u);
while(u--) {
double l, d;
scanf("%lf%lf", &l, &d);
memset(dp, 0, sizeof(dp));
int L = l * T, D = d * T;
double sum = 0;
for(int i = D + 1; i <= L; ++i) {
dp[i] = sum / (i - 1) + i + D;
sum += dp[i];
}
printf("%.2f\n", dp[L] / T);
}
return 0;
}
当时自己想的那个代码恰好也是50万,不过多了一个ceil,导致误差:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int T = 500000;
double dp;
double predp;
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int u;
scanf("%d", &u);
while(u--) {
double l, d;
scanf("%lf%lf", &l, &d);
int D = d * T;
dp = 0.0;
predp = 0.0;
int L = ceil(l * T);
for(int i = D + 1; i <= L; ++i) {
dp = predp / (i - 1) + (d + i / (double)T);
predp += dp;
}
printf("%.2f\n", dp);
}
return 0;
}
去掉无关的常数之后,20万的精度是足够了的。
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int T = 200000;
double dp;
double predp;
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int u;
scanf("%d", &u);
while(u--) {
double l, d;
scanf("%lf%lf", &l, &d);
int D = d * T;
dp = 0.0;
predp = 0.0;
int L = l * T;
for(int i = D + 1; i <= L; ++i) {
dp = predp / (i - 1) + D + i;
predp += dp;
}
printf("%.2f\n", dp / T);
}
return 0;
}