CF264C Choosing Balls

Solution

首先明确的是对每一次询问分开处理。

然后因为要求最大价值,可以想到用DP去做。设 \(d_{i,j}\) 表示前 \(i\) 个元素,以 \(j\) 颜色为结尾的最大价值。可以发现,每一个 \(i\) 最多只会更新一个 \(dp\) ,所以可以将第一维省略掉。

接下来我们思考第 \(i\) 个球会从哪些情况继承也就是状态转移:

1.这是子序列第 \(1\) 个球: \(b\times v_i\)

2.从上一个和它颜色相同的球的结尾的子序列转移: \(dp_{c_i}+a\times v_i\)

3.从和它颜色不一样的球的结尾的最大价值子序列转移: \(\max\limits_{c_i\not=c_j}\{dp_j\}+b\times v_i\)

但是因为3转移单次复杂度为 \(O(n)\) ,所以我们需要 \(O(1)\) 找到最优的那个 \(j\)

因为 \(dp\) 数组在转移后是单调不减的,所以可以保留最大值的颜色。

但是如果只保留转移前最大值的颜色,那么可能最大值的颜色和 \(i\) 一样,所以还要保留次大值的颜色,这样就能保证两个中起码有一个是3转移需要的。

然后更新最大值,次大值颜色, \(dp_{c_i}\) 的值和 \(ans\) 即可。

代码

#include<bits/stdc++.h>
#define int long long

using namespace std;
const int N=1e5+5,INF=1e18;
int n,m,a,b,ans;
int v[N],c[N],dp[N];

signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&v[i]);
    for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
    while(m--){
        ans=0;
        scanf("%lld%lld",&a,&b);
        for(int i=1;i<=n;i++) dp[i]=-INF;
        int t1=0,t2=0,tmp=0;
        for(int i=1;i<=n;i++){
            tmp=max(b*v[i],dp[c[i]]+a*v[i]);
            if(c[i]!=t1) tmp=max(tmp,dp[t1]+b*v[i]);
            else tmp=max(tmp,dp[t2]+b*v[i]);
            if(tmp>dp[t1]){
                if(t1!=c[i]){
                    t2=t1;
                    t1=c[i];
                }
                else t1=c[i];
            }
            else if(tmp>dp[t2]&&c[i]!=t1) t2=c[i];
            dp[c[i]]=max(dp[c[i]],tmp);
            ans=max(ans,tmp);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2020-10-14 21:35  jasony_sam  阅读(104)  评论(0编辑  收藏  举报