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

前言

本来可以赛时 AC 的结果由于少判断了一种条件而错在了 \(3\)\(4\) 这两个点,但是还是要说一声 @As_snow 太强了。

思路

我们发现对于一次查询,第一种操作最多使用一次,然后我们对于在第 \(i\) 个点使用操作一的代价是 \(lc_{i}+a_i+sumb_{i+1}-re_{i+1}\) 这里我们的 \(lc_i\) 代表在 \(1\sim i\) 中在且属于 \(p\) 集合的位置的 \(c\) 数组之和,然后 \(sumb_{i}\) 代表 \(\sum_{j=i}^{n} b_j\) 然后 \(re_i\) 代表在 \(i\sim n\) 中属于 \(p\) 集合的 \(b_j\) 之和,这个写出来会发现是 \(n^2\) 的。

那么我们来想如何优化,我们可以发现对于每一次选择只会出现两种情况。

  • 取的 \(i\) 不属于 \(p\) 集合。
  • 取的 \(i\) 属于 \(p\) 集合。

这里我们可以发现如果是第二种情况可以直接求出,那么我们只需要处理第一种情况,然后我们可以发现对于在 \(p_i+1 \sim p_{i+1}-1\) 之间的数的 \(lc_i\)\(re_i\) 的值是不会变的那么我们的第一种情况只需要求出 \(a_i+sumb_{i+1}\) 的最小值即可,这里我们可以用线段树维护。

但是这里还有三种特殊情况需要特殊处理。

  • 没有用到第一种操作,最小值为 \(sumb_1-re_1\)
  • 使用第一个操作的位置在 \(p_1\) 之前,这里的最小值为 \(Min_{1,p_1-1}-g_1\) 这里的 \(Min_{i,j}\) 就是 \(i\sim j\)\(a_i+sum_{i+1}\) 的最小值。
  • 使用第一个操作在 \(p_cnt\) 之后,这里的处理方式也和上一种像似,就不细讲了。

代码

#include <bits/stdc++.h>
using namespace std ;
#define i12 __int128
#define int long long
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define rep1(i,x,y) for(int i=x;i>=y;i--)
#define fire signed
int n,q;
const int N=5e5+10;
int a[N],b[N],c[N];
i12 g[N];
int in[N];
i12 sum[N];
struct node {
	int l,r;
	i12 Min;
} tr[N<<2];
void up(int x) {
	tr[x].Min=min(tr[x*2].Min,tr[x*2+1].Min);
}
void build(int u,int l,int r) {
	tr[u]= {l,r};
	if(l==r) {
		tr[u].Min=a[l]+sum[l+1];
		return ;
	}
	int mid=l+r>>1;
	build(u*2,l,mid);
	build(u*2+1,mid+1,r);
	up(u);
}
int Ans(int u,int l,int r) {
	if(tr[u].l>=l&&tr[u].r<=r) {
		return tr[u].Min;
	}
	int mid=tr[u].l+tr[u].r>>1,res=LONG_LONG_MAX;
	if(mid>=l) res=min(res,Ans(u*2,l,r));
	if(mid<r) res=min(res,Ans(u*2+1,l,r));
	return res;
}
void print(int x) {
	if(x>=10) print(x/10);
	putchar(x%10+'0');
}
fire main() {
//	freopen("s.out","r",stdin);
//	freopen("s.in","w",stdout);
	scanf("%lld",&n);
	rep(i,1,n) scanf("%lld",&a[i]);
	rep(i,1,n) scanf("%lld",&b[i]);
	rep(i,1,n) scanf("%lld",&c[i]);
	rep1(i,n,1) sum[i]=sum[i+1]+b[i];
	build(1,1,n);
	i12 ans=LONG_LONG_MAX;
	rep(i,1,n) ans=min(ans,a[i]+sum[i+1]);
	scanf("%lld",&q);
	while(q--) {
		int cnt=false;
		scanf("%lld",&cnt);
		if(!cnt) {
			print(ans);
			cout<<endl;
			continue;
		}
		rep(j,1,cnt) scanf("%lld",&in[j]);
		g[cnt+1]=false;
		rep1(i,cnt,1) g[i]=g[i+1]+b[in[i]];
		i12 res=LONG_LONG_MAX,now=0;
		if(cnt==n) {
			cout<<"0\n";
			continue;
		}
		res=Ans(1,1,in[1]-1)-g[1];
		res=min(res,sum[1]-g[1]);
		rep(i,1,cnt) {
			res=min(res,now+Ans(1,in[i-1]+1,in[i]-1)-g[i]);
			now+=c[in[i]];
			res=min(res,now+a[in[i]]+sum[in[i]+1]-g[i+1]);
		}
		res=min(res,now+Ans(1,in[cnt]+1,n));
		print(res);
		cout<<endl;
	}
	return false;
}
/*
7
10 1 6 9 4 2 4
0 5 2 3 0 1 4
4 1 4 1 5 3 5
1
2 2 6
*/
posted @ 2024-01-31 11:43  highkj  阅读(13)  评论(0)    收藏  举报