CF1651F Tower Defense 题解

CF1651F Tower Defence

其实是一道分块题,但是我不想写分块,于是就写了主席树。

暴力是显然的,每一个怪物出现时都让它把塔挨个走一遍即可。现在要优化这个暴力,就要考虑如何快速处理每个怪物的情况。

发现一个怪物会让一段塔的前缀的魔力值变成 \(0\),然后死在一个塔上并让这个塔的魔力值减小一部分,后面的塔不动。那么如果我们把这一段变为 \(0\) 的前缀划分为一个段,每次模拟怪物走过的路时按照段来模拟,不难发现段数是 \(O(n)\) 的,复杂度就是正确的。

现在问题主要在于如何正确维护一段中每个塔当前的魔力值,毕竟每个塔的上限和回复值都是不一样的。这里有一道引子题:CF837G,大体题意为给定 \(n\) 个一次分段函数 \(f_i(x)\),求其中一段函数值的和。做法是以值域为范围、开 \(n\) 棵主席树,每棵主席树维护当前分段函数在一定值域的函数值,即维护常数项和一次项系数,然后在主席树上就能方便地查询一段函数值的和了。那么这道题中,塔的魔力值同样可以看作一个分段函数 \(f_i(x)=\begin{cases}r_ix&x\le\lfloor\frac{c_i}{r_i}\rfloor\\c_i&x>\lfloor\frac{c_i}{r_i}\rfloor\end{cases}\),那么我们要求一段函数的和,同样能套用 CF837G 的方法。

然后这道题就差不多了,我们用栈来维护所有段,对于每个怪物挨个遍历所有段直到它死。如果当前段长度等于 \(1\),显然好维护;如果当前段的长度大于 \(1\),就二分找到怪物死的位置,把这个单点维护一下剩余魔力值,之前的塔全部合并成魔力值为 \(0\) 的一个段即可。

细节不少。最后是因为各种调试而码风极其混乱的代码:

#include<bits/stdc++.h>
#define int long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
using namespace std;

char buf[1<<20],*p1=buf,*p2=buf;
int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
constexpr int MAXN=2e5+5;
int n,q,c[MAXN],r[MAXN];
int rt[MAXN];
int top;
struct{
	int id,nc,lst;
}stk[MAXN];
int now,P;
struct{
	#define lp st[p].lc
	#define rp st[p].rc
	#define lq st[q].lc
	#define rq st[q].rc
	struct SegTree{
		int lc,rc;
		int a,b;
	}st[MAXN<<6];
	int tot;
	void chg(int x,int ka,int kb,int s,int t,int q,int&p){
		st[p=++tot]=st[q];
		st[p].a+=ka,st[p].b+=kb;
		if(s==t) return;
		int mid=(s+t)>>1;
		if(x<=mid) chg(x,ka,kb,s,mid,lq,lp);
		else chg(x,ka,kb,mid+1,t,rq,rp);
	}
	int ask(int l,int r,int x,int s,int t,int q,int p){
		if(l<=s&&t<=r) return (st[p].a-st[q].a)*x+st[p].b-st[q].b;
		int mid=(s+t)>>1,res=0;
		if(l<=mid) res+=ask(l,r,x,s,mid,lq,lp);
		if(mid<r) res+=ask(l,r,x,mid+1,t,rq,rp);
		return res;
	}
}T;

signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		c[i]=read(),r[i]=read();
		T.chg(0,r[i],0,0,2e5+1,rt[i-1],rt[i]);
		if(c[i]/r[i]<=2e5) T.chg(c[i]/r[i]+1,-r[i],c[i],0,2e5+1,rt[i],rt[i]); 
	}
	q=read();
	stk[0]={n+1,0,0};
	for(int i=n;i;i--) stk[++top]={i,c[i],0};
	int ans=0;
	while(q--){
		int t=read(),h=read();
		while(top){
			int L=stk[top].id,R=stk[top-1].id-1,dt=t-stk[top].lst;
			if(L==R){
				int tp=min(c[L],dt*r[L]+stk[top].nc);
				if(tp>h){
					stk[top].nc=tp-h;
					stk[top].lst=t;
					break;
				}else h-=tp,top--;
			}else{
				int fk=T.ask(0,min(dt,200001ll),dt,0,2e5+1,rt[L-1],rt[R]);
				if(fk>h){
					int P=L-1;
					int l=L,r=R;
					while(l<=r){
						int mid=(l+r)>>1;
						if(T.ask(0,min(dt,200001ll),dt,0,2e5+1,rt[L-1],rt[mid])<=h) P=mid,l=mid+1;
						else r=mid-1;
					}
					int now=T.ask(0,min(dt,200001ll),dt,0,2e5+1,rt[L-1],rt[P]);
					P++;
					if(P==R) top--;
					else stk[top].id=P+1;
					h-=now;
					int tp=min(c[P],::r[P]*dt);
					stk[++top]={P,tp-h,t};
					break;
				}else h-=fk,top--;
			}
		}
		if(!top) ans+=h;
		if(stk[top].id!=1) stk[++top]={1,0,t};
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2025-07-13 22:04  Laoshan_PLUS  阅读(230)  评论(0)    收藏  举报