题解:CF1651F Tower Defense
题意:很简单了,不再赘述。
做法:
首先我们注意到一件事情,我们每次往前走一步的时候所有都会恢复,这看起来很麻烦,但是我们注意到一开始全都是满的,考虑两个塔,前面的塔 \(i\) 虽然会被更早碰到但是也会有时间恢复,后面的更晚碰到但是恢复时间也是相同的。
所以其实我们没有必要管每步都要恢复这个事情,我们可以把问题转化为一个怪物会在第 \(t\) 秒到的时候瞬间一路冲过去。
那么很容易想到直接暴力,我们每次一定是推平一个前缀,在某一个位置停下,并且如果我们能快速维护,这样确实是 \(O(n)\) 的。
那么我们这里序列就会被每一次推平分为很多块,每一块形如 \((l,r,v,t)\) 代表这一块是区间 \([l,r]\),区间中还剩下了 \(v\) 的血量,当然这一条仅限于单点的时候,上一次被更新的时候时间为 \(t\)。
那么对于单点我们很容易可以计算出来会消耗多少血,但是对于区间就比较困难,因为区间内有些点是一直在恢复,但是有一些点已经达到上限了。
那么我们考虑将所有塔按达到上限的时间排序建立两颗主席树,第一颗主席树每个位置维护的是原序列中区间中增量之和,第二个维护的是区间中上限之和。
我们按到达上限时间扫描,每次对第一颗线段树上对应位置减掉增量,在第二颗线段树上加上上限。对于询问一个区间过了 \(t\) 时间恢复了多少,我们直接二分找到他是哪个版本,然后直接区间询问增量,这一部分是需要乘上回复时间,再加上上限之和就可以计算出来。
回到原本做答案,当我们需要判断是否能跨过一个颜色段的时候,我们直接按上述方法计算总和即可,对于单点就直接暴力计算即可。如果跨不过颜色段的话我们就在里面二分即可,线段树上二分或者直接二分都可以。
最后记得推平。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5 + 5;
int n, c[maxn], a[maxn], p[maxn], d[maxn], m;
bool cmp(int x, int y) {
return d[x] < d[y];
}
struct node {
int l, r, sum;
};
struct Segtree {
node tr[maxn * 50];
int rt[maxn], tot;
void pushup(int t) {
tr[t].sum = tr[tr[t].l].sum + tr[tr[t].r].sum;
}
int build(int l, int r, int t) {
t = ++tot;
if(l == r) {
tr[t].sum = a[l];
return t;
}
int mid = l + r >> 1;
tr[t].l = build(l, mid, tr[t].l);
tr[t].r = build(mid + 1, r, tr[t].r);
pushup(t);
return t;
}
int modify(int l, int r, int pos, int p, int q, int val) {
p = ++tot;
tr[p] = tr[q];
if(l == r) {
tr[p].sum += val;
return p;
}
int mid = l + r >> 1;
if(pos <= mid)
tr[p].l = modify(l, mid, pos, tr[p].l, tr[q].l, val);
else
tr[p].r = modify(mid + 1, r, pos, tr[p].r, tr[q].r, val);
pushup(p);
return p;
}
int query(int l, int r, int x, int y, int t) {
if(!t || x > y)
return 0;
if(x <= l && r <= y)
return tr[t].sum;
int mid = l + r >> 1;
if(y <= mid)
return query(l, mid, x, y, tr[t].l);
if(mid < x)
return query(mid + 1, r, x, y, tr[t].r);
return query(l, mid, x, y, tr[t].l) + query(mid + 1, r, x, y, tr[t].r);
}
} tree1, tree2;
void build() {
tree1.rt[0] = tree1.build(1, n, 1);
for (int i = 1; i <= n; i++) {
tree1.rt[i] = tree1.modify(1, n, p[i], tree1.rt[i], tree1.rt[i - 1], -a[p[i]]);
tree2.rt[i] = tree2.modify(1, n, p[i], tree2.rt[i], tree2.rt[i - 1], c[p[i]]);
}
}
struct Seg {
int l, r, v, t;
} st[maxn];
int top;
struct mons {
int h, tim;
friend bool operator<(mons x, mons y) {
return x.tim < y.tim;
}
} x[maxn];
int ans = 0;
int cal1(int l, int r, int t) {
int lx = 0, rx = n + 1;
while(lx + 1 < rx) {
int mid = lx + rx >> 1;
if(d[p[mid]] <= t)
lx = mid;
else
rx = mid;
}
// cout << lx << " " << rx << " " << t << " " << d[p[lx]] << " " << tree1.query(1, n, l, r, tree1.rt[lx])<< endl;
return tree1.query(1, n, l, r, tree1.rt[lx]) * t + tree2.query(1, n, l, r, tree2.rt[lx]);
}
int cal(Seg t, int tim) {
// cout << t.l << " " << t.r << " " << t.v << " " << t.t << " " << tim << endl;
if(!t.v)
return cal1(t.l, t.r, tim - t.t);
return min(c[t.l], t.v + (tim - t.t) * a[t.l]);
}
signed main() {
// freopen("test.in", "r", stdin);
// freopen("std.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> c[i] >> a[i], p[i] = i, d[i] = c[i] / a[i] + 1;
sort(p + 1, p + n + 1, cmp);
build();
cin >> m;
for (int i = n; i >= 1; i--)
st[++top] = Seg{i, i, c[i], 0};
for (int i = 1; i <= m; i++)
cin >> x[i].tim >> x[i].h;
// cout << cal1(1, 9, 3) << endl;
sort(x + 1, x + m + 1);
for (int i = 1; i <= m; i++) {
bool f = 1;
//cout << x[i].tim << " " << x[i].h << endl;
for (int j = top; j >= 1; j--) {
int v = cal(st[j], x[i].tim);
// cout << v << " " << st[j].l << " " << st[j].r << " " << st[j].t << endl;
// cout << v << "adsf " << " " << x[i].h << " " << st[j].v << endl;
int t = st[j].t, lx = st[j].l, rx = st[j].r;
if(v < x[i].h)
x[i].h -= v, top--;
else {
int p = st[j].l;
if(st[j].l == st[j].r) {
int v = min(c[st[j].l], st[j].v + (x[i].tim - st[j].t) * a[st[j].l]);
// cout << v << " " << x[i].h << endl;
v -= x[i].h; x[i].h = 0;
st[j] = Seg{p, p, v, x[i].tim};
top = j;
if(p != 1)
st[++top] = Seg{1, p - 1, 0, x[i].tim};
}
else {
int l = st[j].l - 1, r = st[j].r;
while(l + 1 < r) {
int mid = l + r >> 1;
if(cal1(st[j].l, mid, x[i].tim - st[j].t) < x[i].h)
l = mid;
else
r = mid;
}
p = r;
top = j - 1;
if(p + 1 <= rx)
st[++top] = Seg{p + 1, rx, 0, t};
int v = min(c[p], (x[i].tim - t) * a[p]);
st[++top] = Seg{p, p, v - (x[i].h - cal1(lx, p - 1, x[i].tim - t)), x[i].tim};
// cout << p << " " << st[top].v << endl;
if(1 < p)
st[++top] = Seg{1, p - 1, 0, x[i].tim};
x[i].h = 0;
}
// cout << p << endl;
break;
}
}
if(x[i].h)
ans += x[i].h, top = 0, st[++top] = Seg{1, n, 0, x[i].tim};
// cout << x[i].h << "adfsadfasfsadf" << top << endl;
// for (int j = top; j >= 1; j--)
// cout << st[j].l << " " << st[j].r << endl;
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号