CF1651F Tower Defense
洛谷CF1651F Tower Defense
有 n n n座防御塔按 1 1 1到 n n n的顺序排成一列,每座防御塔都有一个能量上限 c i c_i ci和能量回复速率 r i r_i ri。对于一座塔 i i i,每过一秒,它的能量 w i w_i wi就会变成 min ( w i + r i , c i ) \min(w_i+r_i,c_i) min(wi+ri,ci),每座塔在初始时满能量。
有 q q q个怪物,每个怪物都有两个属性 t i t_i ti和 h i h_i hi,表示这个怪物会在第 t i t_i ti秒出现在第一座塔前。当它到第 j j j座塔前时,自己的血量 h i h_i hi会减少 min ( h i , w j ) \min(h_i,w_j) min(hi,wj),塔的能量也会减少这个数。当怪物的血量 h i = 0 h_i=0 hi=0时怪物停止移动,否则它下一秒会移动到下一座塔。
有些怪物在经过塔 n n n后血量仍未减少至 0 0 0,求这样的怪物最终剩下的血量的总和。
注意每座塔是先恢复能量再打怪物。
∀1≤j<q,tj<tj+1
考虑对塔进行分块,当一个怪进入一个块内的塔时,只有两种情况:
我们发现,因为一个怪最多会被打空血一次,所以第二种情况只会出现 O ( q ) O(q) O(q)次。
对于每个块,维护一个推平标记 f l fl fl表示这个块是否被推平,以及 l s t lst lst表示这个块上次被操作的时间戳。预处理出每个块被清零后 t t t秒这个块的能量总和 p r t pr_t prt,对于每个怪依次处理每个块:
我们发现,一开始有 n n n个块没被推平,后面每个怪都会使得有最多一个块从被推平状态变为没被推平状态,也就是说第一种情况只会出现 O ( n + q ) O(n+q) O(n+q)次。每次 O ( n ) O(\sqrt n) O(n)暴力修改,均摊下来的时间复杂度为 O ( ( n + q ) n ) O((n+q)\sqrt n) O((n+q)n)。
每个怪最多遍历 n \sqrt n n个块,所以第二种情况的时间复杂度为 O ( q n ) O(q\sqrt n) O(qn)。
下面考虑如何对每个块求 p r t pr_t prt。因为 1 ≤ t i ≤ 2 × 1 0 5 1\leq t_i\leq 2\times 10^5 1≤ti≤2×105,所以我们只需要求 1 ≤ t ≤ 2 × 1 0 5 1\leq t\leq 2\times 10^5 1≤t≤2×105的 p r t pr_t prt即可。
设当前块在第 0 0 0时刻清零,当前时刻为 t t t,则对于块中的每一座塔 i i i,分为三种情况:
我们考虑维护每一秒的能量增量的差分,做一次前缀和就能得到每一秒的能量增量,再做一次前缀和即可得到 p r t pr_t prt。
这样每求一块的 p r t pr_t prt是 O ( n ) O(n) O(n)的,总共有 n \sqrt n n块,所以预处理的时间复杂度为 O ( n n ) O(n\sqrt n) O(nn)。
虽然时间复杂度是 O ( ( n + q ) n ) O((n+q)\sqrt n) O((n+q)n)的,但这题的空间复杂度是 O ( n n ) O(n\sqrt n) O(nn)的,数组开不下。我们考虑如何降低空间复杂度。
我们可以先把询问离线下来,然后一块一块地做,每块都将每个怪处理一次,这样就只需保留当前块的 p r t pr_t prt和推平标记,空间复杂度就降为 O ( n ) O(n) O(n)了。
总时间复杂度为 O ( ( n + q ) n ) O((n+q)\sqrt n) O((n+q)n),空间复杂度为 O ( n ) O(n) O(n)。
#include
using namespace std;
const int N=200000;
int n,q,bl;
long long ans=0,C[N+5],R[N+5],t[N+5],h[N+5],w[N+5],pr[N+5],v[N+5];
int pos(int i){
return (i-1)/bl+1;
}
void init(int l,int r){
long long wt=0;
for(int i=0;i<=N;i++) v[i]=0;
for(int i=l;i<=r;i++){
wt+=R[i];
if(C[i]/R[i]>=N) continue;
v[C[i]/R[i]+1]+=C[i]%R[i]-R[i];
v[C[i]/R[i]+2]+=-C[i]%R[i];
}
for(int i=1;i<=N;i++){
wt+=v[i];
pr[i]=pr[i-1]+wt;
}
}
void solve(int l,int r){
init(l,r);
int fl=0,lst=0;
for(int i=1;i<=q;i++){
if(!h[i]) continue;
if(!fl||fl&&h[i]<=pr[t[i]-t[lst]]){
fl=1;
for(int j=l;j<=r;j++){
w[j]=min(C[j],w[j]+(t[i]-t[lst])*R[j]);
if(h[i]>=w[j]){
h[i]-=w[j];w[j]=0;
}
else{
w[j]-=h[i];h[i]=0;fl=0;
}
}
}
else h[i]-=pr[t[i]-t[lst]];
lst=i;
}
}
int main()
{
// freopen("dinosaurs.in","r",stdin);
// freopen("dinosaurs.out","w",stdout);
scanf("%d",&n);
bl=sqrt(n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&C[i],&R[i]);
w[i]=C[i];
}
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%lld%lld",&t[i],&h[i]);
}
for(int i=1;i<=pos(n);i++){
solve(i*bl-bl+1,min(i*bl,n));
}
for(int i=1;i<=q;i++) ans+=h[i];
printf("%lld",ans);
return 0;
}