「KDOI-06-S」消除序列 題解

Supor__Shoop發表於2024-09-27

分享一個應該很少人想到的做法。

首先貪心地想,第一種操作肯定最多選擇一次。比如如果選擇了下標 \(i\)\(j\) 進行第一種操作,那麼就等價於在 \(\max\{i,j\}\) 進行了一次操作。由於代價是非負數,則我們最多隻用執行一次。當然也可以不使用這種操作

有了這個思路,我們先考慮不使用第一種操作的最優答案,很明顯就是 \(\begin{aligned} \sum _{i \notin P}b_i\end{aligned}\)

接著再考慮使用一次的情況。我們先假定在位置 \(j\) 處進行一次第一種操作,這就表示 \([1,j]\) 的所有數字都變成了 \(0\),我們將 \(i\in P \wedge i\in [1,j]\) 的所有 \(i\) 統計到集合 \(A\) 中,將 \(i \notin P \wedge i \notin [1,j]\) 的所有 \(i\) 統計到集合 \(B\) 中。這個時候不難發現 \(A\) 中的元素都是應當為 \(1\) 但是因為在 \(j\) 進行了第一種操作後變成 \(0\) 的下標,那麼我們就要把它們全部再變為 \(1\);而在 \(B\) 中的元素則是不應當為 \(1\) 但是還保持初始狀態 \(1\) 的下標,我們需要一個一個地將它們變為 \(0\)。則此時答案就是:

\[\begin{aligned}\min\{a_j+(\sum_{i\in A}c_i)+(\sum _{i\in B}b_i) \}\end{aligned} \]

但是資料範圍不允許我們列舉 \(j\)。其實我們不難發現當 \(j\in [p_i,p_{i+1}-1]\)\(A\)\(B\) 兩個集合是不變的。則代入上述式子唯一改變的就是 \(a_j\) 的值。而且題目中提到 \(\sum m\leq 5\times 10^5\),這說明 \(|A|\) 會很小,但是 \(|B|\) 有可能會很大,因此我們提前預處理出 \(\begin{aligned}sum_i=\sum _{k=1}^ib_k \end{aligned}\),則 \(\begin{aligned} \sum_{i\in B}b_i=sum_n-sum_j-num \end{aligned}\),其中 \(\begin{aligned}num=\sum_{k\in P\wedge k\in [j+1,n]}b_k\end{aligned}\)\(num\) 可以一步一步推得,因此計算量相對減少了很多。我們再代入進原式:

\[\min\{a_j+(\sum_{i\in A}c_i)+sum_n-sum_j-num\} \]

考慮到當前 \(j\) 列舉的區間保證 \(A\)\(B\) 不變,轉換一下得到:

\[\min\{a_j-sum_j\}+(\sum_{i\in A}c_i)-num+sum_n \]

不難發現 \(\min\{a_j-sum_j\}\) 中的 \(a_j\)\(sum_j\) 都不會被修改,因此我們可以用 ST 表記錄 \(a_j-sum_j\) 的最小值。然後我們就可以在規定的資料範圍內透過了。

程式碼如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=5e5+5;
int n,m,q;
int p[MAXN];
int a[MAXN],b[MAXN],c[MAXN];
int sum[MAXN],st[MAXN][20];
int lg[MAXN];
int query(int l,int r)
{
	int k=lg[r-l+1];
	return min(st[l][k],st[r-(1<<k)+1][k]);
}
void read(int &x)
{
	x=0;
	short flag=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')	flag=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=flag;
}
signed main()
{
	read(n);
	for(int i=2;i<=n;i++)	lg[i]=lg[i>>1]+1;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<20;j++)	st[i][j]=1e15;
	}
	for(int i=1;i<=n;i++)	read(a[i]);
	for(int i=1;i<=n;i++)	read(b[i]),sum[i]=sum[i-1]+b[i];
	for(int i=1;i<=n;i++)	read(c[i]);
	for(int i=1;i<=n;i++)	st[i][0]=a[i]-sum[i];
	for(int j=1;j<=19;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]);
	}
	read(q);
	while(q--)
	{
		read(m),p[m+1]=n+1;
		for(int i=1;i<=m;i++)	read(p[i]);
		int minn=sum[n],num=0,rsum=0,minx=n+1;
		for(int i=1;i<=m;i++)	minn-=b[p[i]],rsum+=b[p[i]],minx=min(minx,p[i]);
		if(minx>1)	minn=min(minn,query(1,minx-1)+sum[n]-rsum);
		for(int i=1;i<=m;i++)	num+=c[p[i]],rsum-=b[p[i]],minn=min(minn,query(p[i],p[i+1]-1)+sum[n]+num-rsum);
		cout<<minn<<endl;
	}
	return 0;
}

相關文章