D. Berserk And Fireball(模拟)

题意:
n个战士排成一排,分别有个武力值ai。你有两种法术,一个是火球(花费x个法力,消灭连续k个战士),一个是激怒(花费y个法力,选择相邻两个战士,武力值大的会消灭武力值小的)。求最后留下的战士排列成bi需要的最小法力花费

思路:
因为每个数都不同,所以肯定是划分成多个区间,对每个区间进行操作。
1 ) 1) 1) 如果当前区间的长度 l e n < k len < k len<k
①区间最大值大于左右端点,那么无解。因为无法消去最大值。
②区间最大值小于等于左右端点,消费就是 l e n ∗ y len*y leny
2 ) 2) 2) 如果当前区间的长度 l e n ≥ k len ≥ k lenk
①区间最大值大于左右端点,那么必须花费一个 x x x ,来把最大值消掉。如果火球更优,那么答案就是 l e n / k ∗ x + ( l e n % k ) ∗ y len/k*x + (len\%k)*y len/kx+(len%k)y。可以用最大值把 l e n % k len\%k len%k 个战士消掉,然后用火球消去连续的 k 。一定有解。如果激怒更优,那么答案就是 x + ( l e n − k ) ∗ y x + (len-k)*y x+(lenk)y
②区间最大值小于左右端点,不是必须花费 x x x ,则就直接判断哪种方式更优,同上。

代码有点细节

code

#include
using namespace std;
const int man = 2e5+10;
#define ll long long
int a[man],b[man];

signed main() {
    int n,m;scanf("%d%d",&n,&m);
    int x,k,y;scanf("%d%d%d",&x,&k,&y);
    for(int i = 1;i <= n;++i)scanf("%d",a+i);
    for(int i = 1;i <= m;++i)scanf("%d",b+i);
    a[n+1] = b[m+1] = -1;
    ll ans = 0;
    for(int i = 1,j = 0;i <= m+1;++i){
        int l = j+1,r = j+1,len = 0,maxx =-1;
        while(r<=n+1&&a[r]!=b[i])maxx = max(maxx,a[r]),r++;
        if(r>n+1){ans = -1;break;}
        len = r - l;
        if(len<k){
            if(maxx>max(a[l-1],a[r])){ans = -1;break;}
            else ans += 1ll*y*len;
        }else{
            if(maxx>max(a[l-1],a[r])){
                ans += 1ll*x;
                len -= k;
                if(x<1ll*k*y)ans += 1ll*(len/k)*x + 1ll*(len%k)*y;
                else ans += 1ll*y*len;
            }else{
                if(x<1ll*k*y)ans += 1ll*(len/k)*x + 1ll*(len%k)*y;
                else ans += 1ll*y*len;
            }
        }
        if(a[r]!=b[i])j = r + 1;
        else j = r;
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(贪心,思维,codeforce)