zoukankan      html  css  js  c++  java
  • BZOJ4141 THUSC2013 魔塔 贪心

    没得传送门


    考虑当(Atk)增大时,(Def)一定越来越没用,因为回合数在变少。所以考虑从小到大枚举(Atk)然后双指针计算。

    (f_i(x))表示在(Atk = i)时,(Def)(x-1)(x)时可以减少的血量的数量,易知(f_i(x) leq f_i(x - 1) , f_i(x) leq f_{i-1}(x))。对于每一个怪,当(Atk)确定之后,它的攻击回合数就确定了。对于(Atk = i),一个怪物的攻击力为(atk),攻击回合数为(T)时,相当于在函数(f_i(x))([1,atk])区间做一个前缀加(T)的操作。当攻击回合数减小的时候也相当于前缀减然后前缀加。

    不妨把这个问题变得更形象一点:有若干条线段,左端点为(1),右端点不固定,每一条线段都有一个权值。从(Def = x - 1)(Def = x)能够减少的血量就是覆盖了(x)点的所有线段的权值和。

    一件显然的事情是因为(Def)在不断变小,所以之前被当前点覆盖的线段在之后也一定被覆盖。

    所以我们可以设(num_i)表示右端点为(i)的所有线段的权值之和,然后我们考虑:当当前的(Def)覆盖的线段的权值之和(leq cd)时将(Def - 1),然后把(num_{Def - 1})加入,就可以更新出新的权值了。对于删线段和加线段只需要看一下当前加入线段的右端点和(Def)的关系并进行更新就可以了。在维护攻击防御值的时候同时维护血量以保证复杂度。

    那么最后的问题就是如何快速加入和删除线段,暴力做是(O(NHp))的,但是注意到(lceil frac{Hp}{i} ceil = lfloor frac{Hp - 1}{i} floor + 1)只有(sqrt{Hp})种取值,我们可以预处理出每一个转移点,就可以做到(O(sqrt{Hp}N))

    可能比较卡空间,慎用STL

    然后这道题BZOJ上没有SPJ,面向数据编程可以知道:对于所有可行答案,取其中Atk最大的;对于所有Atk一样大的,取Def最大的。

    #include<bits/stdc++.h>
    //this code is written by Itst
    using namespace std;
    
    int read(){
    	int a = 0; char c = getchar();
    	while(!isdigit(c)) c = getchar();
    	while(isdigit(c)){
    		a = a * 10 + c - 48; c = getchar();
    	}
    	return a;
    }
    
    const int _ = 2e6 + 7;
    #define PII pair < int , int >
    struct node{
    	int nxt , rht , val;
    }now[_ * 5 + 5000];
    int head[_] , N , CA , CD , cnt , MXD;
    long long num[_] , Ans , HP , ATK , DEF , sum , X , Y , Z;
    
    void push(int rht , int val , int id){
    	now[++cnt] = (node){head[id] , rht , val};
    	head[id] = cnt;
    }
    
    void modify(int plc , int val){
    	num[plc] += val;
    	if(Y <= plc){sum += val; Z += (plc - Y) * val;}
    }
    
    int main(){
    	N = read(); CA = read(); CD = read();
    	for(int i = 1 ; i <= N ; ++i){
    		int h = read() - 1 , a = read() , d = read();
    		for(int j = 1 , pj ; j <= h ; j = pj + 1){
    			pj = h / (h / j);
    			if(j == 1) push(a , h + 1 , d + 1);
    			else push(a , h / j - h / (j - 1) , d + j);
    		}
    		push(a , -1 , h + d + 1);
    		MXD = max(MXD , d);
    	}
    	Ans = 1e18; Y = 1e6; Z = 1;
    	for(X = 1 ; X <= 2e6 ; ++X){
    		for(int i = head[X] ; i ; i = now[i].nxt)
    			modify(now[i].rht , now[i].val);
    		if(X <= MXD) continue;
    		while(Y > 1 && sum < CD){
    			Z += sum; sum += num[--Y];
    		}
    		if(Z + X * CA + Y * CD <= Ans){
    			Ans = Z + X * CA + Y * CD;
    			ATK = X; DEF = Y; HP = Z;
    		}
    	}
    	cout << HP << ' ' << ATK << ' ' << DEF;
    	return 0;
    }
    
  • 相关阅读:
    NYOJ 625 笨蛋的难题(二)
    NYOJ 102 次方求模
    ZJU Least Common Multiple
    ZJUOJ 1073 Round and Round We Go
    NYOJ 709 异形卵
    HDU 1279 验证角谷猜想
    BNUOJ 1015 信息战(一)——加密程序
    HDU 1202 The calculation of GPA
    "蓝桥杯“基础练习:字母图形
    "蓝桥杯“基础练习:数列特征
  • 原文地址:https://www.cnblogs.com/Itst/p/11110944.html
Copyright © 2011-2022 走看看