P9744 「KDOI-06-S」消除序列 题解

Desciption

给定一个长度为 i i i 的序列 v 1 , v 2 , … , v n v_1,v_2,\dots,v_n v1,v2,,vn,初始时所有元素的值都为 1 1 1

对于下标 i i i 3 3 3 种操作:

  • v 1 , v 2 , … , v i v_1,v_2,\dots,v_i v1,v2,,vi 的值变为 0 0 0,费用是 a i a_i ai
  • v i v_i vi 的值变为 0 0 0,费用是 b i b_i bi
  • v i v_i vi 的值变为 1 1 1,费用是 c i c_i ci

q q q 次询问,每次询问给定一个大小为 m m m 的集合 P P P,问最少需要多少费用,使得 i ∈ P , v i = 1 ( 1 ≤ i ≤ n ) , j ∉ P , v j = 0 ( 1 ≤ j ≤ n ) i\in P,v_i=1(1\le i\le n),j\notin P ,v_j=0(1\le j\le n) iP,vi=1(1in),j/P,vj=0(1jn)

Solution

引理:在一次询问内,第一种操作最多只会进行一次。

i , j ( i < j ) i,j(i<j) i,j(i<j) 分别进行了一次操作一。

如果先在 j j j 进行操作一,就已经把 v 1 , … , v i v1,\dots ,vi v1,,vi 变为 0 0 0 了,再操作是不优的。

所以遍历每一个 i i i,发现在 i i i 做操作一时,答案为 a i + ∑ j = i + 1 , j ∉ P n b j + ∑ j = 1 , j ∈ P i c j a_i+\sum\limits_{j=i+1,j\notin P}^n b_j+\sum\limits_{j=1,j\in P}^{i} c_j ai+j=i+1,j/Pnbj+j=1,jPicj,这个式子可以用前缀和优化。

s u m 1 i = ∑ j = 1 i b j ( 1 ≤ i ≤ n ) , s u m 2 i = ∑ j = 1 m b P j ( 1 ≤ i ≤ m ) , s u m 3 i = ∑ j = 1 m c P − j ( 1 ≤ i ≤ m ) sum1_i=\sum\limits_{j=1}^i b_j(1\le i\le n),sum2_i=\sum\limits_{j=1}^mb_{P_j}(1\le i\le m),sum3_i=\sum\limits_{j=1}^m c_{P-j}(1\le i\le m) sum1i=j=1ibj(1in),sum2i=j=1mbPj(1im),sum3i=j=1mcPj(1im)

式子转换为 a i + s u m 1 n − s u m 1 i − ( s u m 2 m − s u m 2 j ) + s u m 3 j ( P j ≤ i , P j + 1 > i ) a_i+sum1_n-sum1_i-(sum2_m-sum2_j)+sum3_j(P_j\le i,P_{j+1}>i) ai+sum1nsum1i(sum2msum2j)+sum3j(Pji,Pj+1>i)

此时时间复杂度为 O ( q n ) O(qn) O(qn),需要进一步优化。

发现对于在 P i P_i Pi P i + 1 P_{i+1} Pi+1 之间操作操作一(不含两端), j j j 是相同的,所以我们只需预处理求出 g k = a k + s u m 1 n − s u m 1 k g_k=a_k+sum1_n-sum1_k gk=ak+sum1nsum1k,再处理出区间内 g g g 的最小值。可以用线段树或 ST表维护。

此时对于 i i i,得出 [ P i + 1 , P i + 1 − 1 ] [P_i+1,P_{i+1}-1] [Pi+1,Pi+11] g g g 的最小值再加上式子中其他的值即可, [ P m + 1 , n ] [P_m+1,n] [Pm+1,n] 也要计算。

还要计算刚好在 i i i 上做操作一时的值,没有操作一时的值以及没有操作二时的值,所有可能答案取最小即可。

时间复杂度 O ( q m ) O(qm) O(qm)

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值