CF1651F Tower Defense

一、题目

点此看题

二、解法

考虑颜色段均摊,维护场上的若干个连续段 \([l,r]\),可以按照左端点降序排列,这样询问时类似弹栈做就行了。

如果遇到 \(l=r\),这代表了一个单点,可以直接暴力计算。遇到 \(l<r\)这代表被以前的询问推平的一段区间,问题可以转化成给定初始生命 \(hp\),给定时间差 \(T\),给定左右端点 \([l,r]\),问是否能再次推平这个区间,或者是在这个区间停下。

关键在于在某个时间差 \(T\) 下计算 \([l,r]\) 之间的权值和。每个塔关于时间的权值可以看成一个分段函数,当 \(T\leq \lfloor\frac{c}{r}\rfloor\) 是斜率为 \(r\),截距为 \(0\) 的一次函数;当 \(T>\lfloor\frac{c}{r}\rfloor\) 是一个截距为 \(c\) 的常函数。

由于 \(t_i\leq 2\cdot 10^5\),我们可以预处理以位置为下标的可持久化线段树,这样每个塔可以转化为两个单点修改,线段树上维护一次函数即可,合并就是直接加。

那么原来的问题可以用线段树上二分来解决,时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 200005;
const int N = 30*M;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,q,T,p,dt[M],c[M],s[M],tim[M],nc[M];
int ans,cnt,rt[M],k[N],b[N],ls[N],rs[N];
vector<int> v[M];
void build(int &x,int l,int r)
{
	x=++cnt;
	if(l==r) {k[x]=dt[l];b[x]=0;return ;}
	int mid=(l+r)>>1;
	build(ls[x],l,mid);
	build(rs[x],mid+1,r);
	k[x]=k[ls[x]]+k[rs[x]];
}
void ins(int &x,int y,int l,int r,int p)
{
	x=++cnt;k[x]=k[y];b[x]=b[y];
	ls[x]=ls[y];rs[x]=rs[y];
	if(l==r) {k[x]=0;b[x]=c[l];return ;}
	int mid=(l+r)>>1;
	if(mid>=p) ins(ls[x],ls[y],l,mid,p);
	else ins(rs[x],rs[y],mid+1,r,p);
	k[x]=k[ls[x]]+k[rs[x]];
	b[x]=b[ls[x]]+b[rs[x]];
}
int ask(int x,int l,int r,int L,int R)
{
	if(!x || l>R || L>r) return 0;
	if(L<=l && r<=R) return T*k[x]+b[x];
	int mid=(l+r)>>1;
	return ask(ls[x],l,mid,L,R)+ask(rs[x],mid+1,r,L,R);
}
void find(int x,int l,int r,int L,int &hp)
{
	int mid=(l+r)>>1;
	if(L<=l)
	{
		int w=T*k[x]+b[x];
		if(w<=hp) {p=r;hp-=w;return ;}
		if(l==r) return ;
		if(find(ls[x],l,mid,L,hp),p==mid)
			find(rs[x],mid+1,r,L,hp);
	}
	else if(mid<L) find(rs[x],mid+1,r,L,hp);
	else if(find(ls[x],l,mid,L,hp),p==mid)
		find(rs[x],mid+1,r,L,hp);
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		c[i]=read();dt[i]=read();int j=c[i]/dt[i]+1;
		if(j<M) v[j].push_back(i);
	}
	build(rt[0],1,n);
	for(int i=1;i<M;i++)
	{
		rt[i]=rt[i-1];
		for(int x:v[i]) ins(rt[i],rt[i],1,n,x);
	}
	q=read();s[0]=n+1;
	for(int i=n;i>=1;i--) s[++m]=i,nc[m]=c[i];
	while(q--)
	{
		int t=read(),h=read();
		while(m)
		{
			int i=s[m],j=0;T=t-tim[m];
			if(s[m-1]-i==1)//l==r
			{
				j=min(c[i],dt[i]*T+nc[m]);
				if(j>h) {nc[m]=j-h;tim[m]=t;break;}
				m--;h-=j;continue;
			}
			j=ask(rt[T],1,n,i,s[m-1]-1);
			if(j>h)
			{
				p=i-1;find(rt[T],1,n,i,h);p++;
				if(p==s[m-1]-1) m--;else s[m]=p+1;
				s[++m]=p;tim[m]=t;
				nc[m]=min(c[p],dt[p]*T)-h;break;
			}
			h-=j;m--;
		}
		if(!m) ans+=h;
		if(s[m]!=1) s[++m]=1,tim[m]=t,nc[m]=0;
	}
	printf("%lld\n",ans);
}
posted @ 2022-07-07 16:24  C202044zxy  阅读(285)  评论(1编辑  收藏  举报