• CF1651F Tower Defense


    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 ≤ n , q ≤ 2 × 1 0 5 1\leq n,q\leq 2\times 10^5 1n,q2×105
    • 1 ≤ c i , r i ≤ 1 0 9 1\leq c_i,r_i\leq 10^9 1ci,ri109
    • 1 ≤ h i ≤ 1 0 12 1\leq h_i\leq 10^{12} 1hi1012
    • 0 ≤ t i ≤ 2 × 1 0 5 0\leq t_i\leq 2\times 10^5 0ti2×105
    • ∀ 1 ≤ j < q , t j < t j + 1 \forall 1\leq j∀1j<q,tj<tj+1

    题解

    考虑对塔进行分块,当一个怪进入一个块内的塔时,只有两种情况:

    • 块内所有塔把怪打了一遍之后怪还有血,这时可以看作块中所有塔内的能量都被清零了
    • 怪被块中的一座塔打到血量为 0 0 0,此时这个怪不会再对后面的块产生贡献

    我们发现,因为一个怪最多会被打空血一次,所以第二种情况只会出现 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 1ti2×105,所以我们只需要求 1 ≤ t ≤ 2 × 1 0 5 1\leq t\leq 2\times 10^5 1t2×105 p r t pr_t prt即可。

    设当前块在第 0 0 0时刻清零,当前时刻为 t t t,则对于块中的每一座塔 i i i,分为三种情况:

    • 1 ≤ t ≤ ⌊ c i r i ⌋ 1\leq t\leq \lfloor\dfrac{c_i}{r_i}\rfloor 1trici,则从 t − 1 t-1 t1 t t t这一单位时间,塔 i i i贡献了 r i r_i ri的能量
    • t = ⌊ c i r i ⌋ + 1 t=\lfloor\dfrac{c_i}{r_i}\rfloor+1 t=rici+1,则从 t − 1 t-1 t1 t t t这一单位时间,塔 i i i贡献了 c i   m o d   r i c_i\bmod r_i cimodri的能量
    • t ≥ ⌊ c i r i ⌋ + 2 t\geq \lfloor\dfrac{c_i}{r_i}\rfloor+2 trici+2,则从 t − 1 t-1 t1 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)

    code

    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
  • 相关阅读:
    文件共享服务NFS(服务名nfs,端口tcp/2049)
    【算法leetcode】2341. 数组能形成多少数对(多种语言双百)
    学习笔记:吴恩达ChatGPT提示工程
    做一个答题pk小程序多少钱
    STL:set/multiset容器详解
    6.DesignForPlacement\ExportHighlightedList
    (178)Verilog HDL:设计一个计数器之exams/ece241_2014_q7a
    化工&python | PID控制器优化算法
    通过劫持线程arena实现任意地址分配 n1ctf2018_null
    【推荐】SpringMVC与JSON数据返回及异常处理机制的使用
  • 原文地址:https://blog.csdn.net/tanjunming2020/article/details/133870151