P9744 「KDOI-06-S」消除序列 题解
Desciption
给定一个长度为 的序列 ,初始时所有元素的值都为 。
对于下标 有 种操作:
- 将 的值变为 ,费用是 。
- 将 的值变为 ,费用是 。
- 将 的值变为 ,费用是 。
有 次询问,每次询问给定一个大小为 的集合 ,问最少需要多少费用,使得 。
Solution
引理:在一次询问内,第一种操作最多只会进行一次。
设 分别进行了一次操作一。
如果先在 进行操作一,就已经把 变为 了,再操作是不优的。
所以遍历每一个 ,发现在 做操作一时,答案为 ,这个式子可以用前缀和优化。
设 。
式子转换为 。
此时时间复杂度为 ,需要进一步优化。
发现对于在 和 之间操作操作一(不含两端), 是相同的,所以我们只需预处理求出 ,再处理出区间内 的最小值。可以用线段树或 ST
表维护。
此时对于 ,得出 内 的最小值再加上式子中其他的值即可, 也要计算。
还要计算刚好在 上做操作一时的值,没有操作一时的值以及没有操作二时的值,所有可能答案取最小即可。
时间复杂度 。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long //记得开long long
int n,q;
int a[500050],b[500050],c[500050];
int p[500050];
int lg[500050],st[500050][22];
int sum1[500050],sum2[500050],sum3[500050];
int get_st(int l,int r){
if(l>r) return 100000000000;
int len=lg[r-l+1];
return min(st[l][len],st[r-(1<<(len))+1][len]);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=2;i<=n;i++){
lg[i]=lg[i>>1]+1;
}
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
sum1[i]=sum1[i-1]+b[i];
}
for(int i=1;i<=n;i++){
cin>>c[i];
st[i][0]=sum1[n]-sum1[i]+a[i]; //得出g值
}
for(int j=1;j<=lg[n];j++){
for(int i=1;i+(1<<j)-1<=n;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); //预处理ST表求区间内最小的g
}
}
cin>>q;
while(q--){
int m;
cin>>m;
for(int i=1;i<=m;i++){
cin>>p[i];
sum2[i]=sum2[i-1]+b[p[i]];
sum3[i]=sum3[i-1]+c[p[i]];
}
int ans=100000000000;
for(int i=1;i<=m;i++){
ans=min(ans,min(get_st(p[i-1]+1,p[i]-1)-sum2[m]+sum2[i-1]+sum3[i-1],a[p[i]]+sum1[n]-sum1[p[i]]-(sum2[m]-sum2[i])+sum3[i])); //min内第一个数是取区间,第二个数是刚好取pi
}
ans=min(ans,sum1[n]-sum2[m]); //没有操作一
ans=min(ans,get_st(p[m]+1,n)+sum3[m]); //没有操作二
cout<<ans<<endl;
}
return 0;
}
本文作者:larryyu_blog
本文链接:https://www.cnblogs.com/larryyu/p/17786800.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
分类:
标签:
,
,
,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步