露西亚的训练计划
设低,中,高三种强度的训练编号分别为 1,2,3。
乱搞DP。设 dp i,j 表示训练的第 i 天选择 j 强度的训练方案得到的最大训练效果。则转移方程为:
dp i,j = max(dp i-1,1 , dp i-1, 2 , dp i-1, 3) (j ≠ 3)
dp i,j = dp i-1,1 (j = 3)
搞定。
摧毁车厢
外加手写高精。
终末祈愿
一眼就得是二分。
二分半径,考虑如何check。
显然,对于每次攻击,攻击的区间一定从最左边开始是最优的。举个栗子例子:
现在有3只感染体,分别在1 , 10, 100 的位置,且生命值均为1,单次攻击区间长度为20,伤害为1。
那么第一次攻击一定是攻击(1,20)的位置,因为最左边的感染体位置为1,那么攻击区间再向左偏移就没有意义了,还不如多向后边攻击一下。接下来确定单次攻击在数组中的下标范围。这里数据比较水,枚举每个点进行二分也卡得过去,但是正解是用尺取法。最后的模拟用前缀和作差,统计出半径为d时需要的最小次数,最后与m作对比即可。
std:
#include<bits/stdc++.h> #define int long long #define File(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout) using namespace std; int read(){ int sum = 0, f = 1; char ch = getchar(); for(;!isdigit(ch);ch = getchar()) if(ch == '-') f = -1; for(;isdigit(ch);ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48); return sum * f; } void write(int x){ if(x < 0) putchar('-'), x = -x; if(x > 9) write(x / 10); putchar('0' + x % 10); return; } const int N = 1e6 + 10; int n, m, k, f[N], c[N]; struct node{ int x, h; }a[N], target[N]; bool cmp(node x, node y){ return x.x < y.x; } bool check(int d){ memset(f, 0, sizeof(f)); memset(c, 0, sizeof(c)); for(int i = 1; i <= n; ++ i) a[i] = target[i]; a[n + 1].x = 0x3f3f3f3f3f3f; int l, r = 1; for(l = 1; l <= n; ++ l){ while(r <= n && a[r + 1].x - a[l].x <= 2 * d) ++ r; f[l] = r; } int t = 0, s = 0; for(int i = 1; i <= n; ++ i){ t += c[i]; a[i].h += t; if(a[i].h <= 0) continue; int p = (a[i].h + k - 1) / k; s += p; t -= p * k; c[i] += -p * k; c[f[i] + 1] += p * k; } return s <= m; } signed main(){ n = read(), m = read(), k = read(); for(int i = 1; i <= n; ++ i){ target[i].x = read(); target[i].h = read(); } sort(target + 1, target + n + 1, cmp); target[n + 1].x = 0x3f3f3f3f3f; int l = 0, r = 1e9, mid; while(l + 1 < r){ mid = l + r >> 1; if(check(mid)) r = mid; else l = mid; } if(check(l)) write(l); else write(r); return 0; }
不明数据聚集体:
略,机房讲。
。. ���,�