zoukankan      html  css  js  c++  java
  • BZOJ 3897: Power

    3897: Power

    Time Limit: 30 Sec  Memory Limit: 512 MB
    Submit: 218  Solved: 83
    [Submit][Status][Discuss]

    Description

    我们假设小T有一个人体耐力上限E,在月初的时候,小T有E的体力。
    接下来每一天有一个任务,这个任务小T可以付出任意的非负整数体力去完成,并且,每一天的结束的时候,小T会增加R的体力。当然体力是不可能超出E的,也就是说,如果当前体力+R大于E,那么恢复完之后的体力依旧是E。毫无疑问,体力是不可能小于0的。
    每个任务会有一个价值V[],一个任务的收获就是这个任务的价值乘上付出的体力。
    你要帮帮小T,使他最大化 “所有任务的收获之和”, 方便他继续的高富帅!
    最后,我们的口号是“烧死GFS~”。

    Input

    第一行一个正整数case,表示数据的组数。
    对于每一组数据,第一行有三个正整数E,R,N,表示的是能量上限,恢复值,和这个月的天数。第二行有N个非负整数表示V[1]-V[N]。

    Output

    对于一组数据,一行输出最大化的收获之和。

    Sample Input

    1
    5 2 2
    2 1

    Sample Output

    12

    HINT

    第一天用5的体力,接下来恢复2点体力,再用光。

    Can<=10,N<=500000,E<=10^6.所有的输入非负,并且,V<=10^6。

    Source

    By 佚名提供

    分析:

    考虑我们一定是让大的权值尽量被提供较多的体力,并且要求不能溢出,那么考虑分治的思想,定义$f(l,r,be,en)$为从第l天到第r天,初始体力为be结束时体力为en的最优解,我们选择这个区间中权值最大的那个点x,如果这一天前面发那些天都休养生息还不能满足使得第x天的初始体力为E,那么前面几天就歇着吧,否则就是溢出了,溢出是浪费,所以就递归到前面那几天去贡献,后面几天也是一样的...

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    //by NeighThorn
    using namespace std;
    
    const int maxn=500000+5;
    
    int n,E,R,cas,v[maxn],st[maxn][25];
    
    inline bool cmp(int x,int y){
    	return v[x]<v[y];
    }
    
    inline void init(void){
    	for(int i=1;i<=n;i++)
    		st[i][0]=i;
    	for(int j=1;j<=20;j++)
    		for(int i=1;i+(1<<j-1)<=n;i++)
    			st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1],cmp);
    }
    
    inline int query(int x,int y){
    	if(x>y)
    		swap(x,y);
    	int len=y-x+1,k;
    	for(k=20;k>=0;k--)
    		if(((len>>k)&1)||k==0)
    			break;
    	return max(st[x][k],st[y-(1<<k)+1][k],cmp);
    }
    
    inline long long solve(int l,int r,int be,int en){
    	if(l>r)
    		return 0;
    	int id=query(l,r);
    	long long ans=0,tmp=1LL*(id-l)*R;
    	if(1LL*be+1LL*tmp>E)
    		ans=solve(l,id-1,be,E),be=E;
    	else
    		be+=tmp;
    	tmp=1LL*(r-id+1)*R;
    	if(tmp<en)
    		en-=tmp;
    	else
    		ans+=solve(id+1,r,R,en),en=0;
    	ans+=1LL*(be-en)*v[id];
    	return ans;
    }
    
    signed main(void){
    	scanf("%d",&cas);
    	while(cas--){
    		scanf("%d%d%d",&E,&R,&n);
    		for(int i=1;i<=n;i++)
    			scanf("%d",&v[i]);
    		init();printf("%lld
    ",solve(1,n,E,0));
    	}
    	return 0;
    }
    

      


    By NeighThorn

  • 相关阅读:
    Lesson 43-44 Vacation Season is Approaching?
    Lesson 41-42 How would you respond?
    Lesson 37-38 Do you want to be a millionaire?
    Lesson 35-36 What did you forget?
    Lesson 33-34 Dieting
    保送
    陈老师搬书
    水题(原 USACO Mother's Milk)
    最大公约数和最小公倍数问题(luogu 1029)
    最大子矩阵(OJ 1768)
  • 原文地址:https://www.cnblogs.com/neighthorn/p/6516842.html
Copyright © 2011-2022 走看看