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);
}
标签:rs,int,--,mid,++,Defense,ls,CF1651F,Tower 来源: https://www.cnblogs.com/C202044zxy/p/16455138.html