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;
}