『题解』Luogu P9228 原神

题目传送门

题面大意

有一位魔法师可以打出 \(n\) 次火元素伤害和 \(m\) 次冰元素伤害,分别为 \(a_1,a_2,\dots,a_n\)\(b_1,b_2,\dots,b_m\)

《元素反应论》(节选)

  • 每次元素攻击可以给没有元素附着的怪物附着相应元素。初始时怪物没有元素附着。

  • 用火元素攻击有冰元素附着的怪物,伤害 \(\times 2\),清空元素附着。

  • 用冰元素攻击有火元素附着的怪物,伤害 \(+k\),清空元素附着。

请通过调整攻击顺序最大化总伤害。

\(1 \le n,m \le 10^6,0 \le a_i,b_i,k \le 10^9\)

思路

一道简单贪心~

定义『融化』表示连续两次攻击,第一次产生元素附着,第二次触发元素反应,两次攻击的元素不同。

明显的,尽可能地用火打冰或冰打火可以使总伤害更大,要尽可能打『融化』。

用火元素攻击有冰元素附着的怪物,伤害 \(\times 2\),清空元素附着。

可见,火元素伤害值越高,越有利于『融化』,所以我们将 \(a[1\dots n]\) 排序,『融化』的第二次攻击要优先从 \(a\) 的末尾选择。我们可以用两个指针 \(al,ar\) 分别指向 \(a\) 的最左侧和最右侧,『融化』的第一次攻击优先用 \(al\) 所指向的元素,第二次则优先用 \(ar\) 指向的,注意维护 \(al\)\(ar\)

用冰元素攻击有火元素附着的怪物,伤害 +k,清空元素附着。

冰打火的收益是一个常数,所以冰元素攻击用来附着还是反应对总伤害没有影响,所以不必对其排序。为了不犯不必要的错误,同样用两个指针 \(bl,br\) 分别指向 \(b\) 的左侧和右侧,『融化』的第一次攻击用 \(bl\),第二次 \(br\)(这里用 \(bl\)\(br\) 可以随意,但为了与火保持一致,就不动它了)。

先抛开上面的东西,对于一次火元素伤害 \(x\) 和一次冰元素伤害 \(y\),怎么安排在『融化』中的顺序才能使收益最大化呢?

可以得到火打冰的收益为 \(x\),冰打火的收益为 \(k\),哪个大选哪个即可。

以上述方法尽可能打完『融化』后,再将剩余的 \(|n-m|\) 次伤害计入答案即可。

代码

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n,m,k;
ll a[N],b[N],ans;
int al,ar,bl,br;

int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
    for(int i=1; i<=m; i++) scanf("%lld",&b[i]);
    sort(a+1,a+n+1);
    al=bl=1,ar=n,br=m;
    while(al<=ar && bl<=br)
        if(a[ar]<k){ // 冰打火
            ans+=a[al++]; // 火附着
            ans+=b[br--]+k; // 冰融化
        }else{ // 火打冰
            ans+=b[bl++]; // 冰附着
            ans+=a[ar--]*2; // 火融化
        }
    if(al<=ar) for(int i=al; i<=ar; i++) ans+=a[i];
    else for(int i=bl; i<=br; i++) ans+=b[i];
    printf("%lld\n",ans);
    return 0;
}
posted @ 2023-04-23 21:53  仙山有茗  阅读(112)  评论(1编辑  收藏  举报