题解: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;
}
posted @ 2025-08-04 14:46  LUlululu1616  阅读(14)  评论(0)    收藏  举报