[NOIP2025] 序列询问

给定长度为 \(n\) 的序列 \(A\)。有 \(q\) 次询问,每次询问给定区间 \([L,R]\),你需要对所有 \(i \in [1,n]\) 求出 \(\max\limits_{1 \leq l \leq i \leq r \leq n \land r-l+1 \in [L,R]} \sum\limits_{j=l}^{r} a_j\)

\(1 \leq n \leq 5 \times 10^4\)\(1 \leq q \leq 1024\)\(|a_i| \leq 10^5\)

严肃对 Purslane 题解进行大学习。

考虑 \(2L \geq R\) 的时候怎么做。发现此时一个包含 \(i\) 的区间 \([l,r]\) 必然满足 \(i-l+1 \leq L\)\(r-i+1 \leq L\)。若否,则有 \(i-l+1 > L\)\(r-i+1 > L\),可得 \(r-l+1 \geq 2L+1\)。而 \(r-l+1 \leq R\),得到 \(2L+1 \leq R\),与 \(2L \geq R\) 矛盾。

接下来我们发现满足 \(l \in [i-L+1,i]\)\(r \in [i,i+L-1]\) 的合法区间都包含 \(i\),这一点是显然的。此时我们可以对于每个 \(i\),预处理出以 \(i\) 为左端点或右端点的合法区间的 \(\sum\limits_{j=l}^{r} a_j\) 最大值,查询是滑动窗口的形式。容易发现,预处理的部分也是滑动窗口的形式,此时我们可以在 \(O(n)\) 的时间复杂度内解决问题。

接下来我们考虑没有特殊性质的情况,可以简单进行二进制分拆,这样就可以做到 \(O(qn\log n)\)。我们考虑更牛一点,在询问时拆成 \([L,2^p]\)\([2^p,2^q]\)\([2^q,R]\) 三个区间,就可以在 \(O(n \log^2 n)\) 的时间内进行预处理,查询是 \(O(n)\) 的,可以做到 \(O(n\log^2 n+qn)\) 的时间复杂度。

放一个被卡常的代码。

#include<iostream>
#include<cstdio>
using namespace std;
const long long INF=-0x3f3f3f3f3f3f3f3f;
int n,pow_2[16],log_n;
long long a[50010],suml[50010],sumr[50010],ans[50010],final_ans[50010],table[16][16][50010];
int dq[50010],head,tail;
long long l_i[50010],r_i[50010];
void solve(int L,int R){
	for(int i=1;i<=n;i++){
		suml[i]=suml[i-1]+a[i];
	}
	for(int i=n;i>=1;i--){
		sumr[i]=sumr[i+1]+a[i];
	}
	for(int i=1;i<=n;i++){
		ans[i]=l_i[i]=r_i[i]=INF;
	}
	int len=R-L+1;
	head=0;
	tail=1;
	for(int i=1;i<=n+len-1;i++){
		while(tail<=head  &&  dq[tail]<i-len+1){
			tail++;
		}
		if(i<=n){
			while(tail<=head  &&  suml[dq[head]]<suml[i]){
				head--;
			}
			dq[++head]=i;
		}
		if(i>=R){
			l_i[i-R+1]=suml[dq[tail]]-suml[i-R];
		}
	}
	head=0;
	tail=1;
	for(int i=n;i>=1-len+1;i--){
		while(tail<=head  &&  dq[tail]>i+len-1){
			tail++;
		}
		if(i>=1){
			while(tail<=head  &&  sumr[dq[head]]<sumr[i]){
				head--;
			}
			dq[++head]=i;
		}
		if(i<=n-R+1){
			r_i[i+R-1]=sumr[dq[tail]]-sumr[i+R];
		}
	}
	head=0;
	tail=1;
	for(int i=1;i<=n;i++){
		while(tail<=head  &&  dq[tail]<i-L+1){
			tail++;
		}
		while(tail<=head  &&  l_i[dq[head]]<l_i[i]){
			head--;
		}
		dq[++head]=i;
		ans[i]=max(ans[i],l_i[dq[tail]]);
	}
	head=0;
	tail=1;
	for(int i=n;i>=1;i--){
		while(tail<=head  &&  dq[tail]>i+L-1){
			tail++;
		}
		while(tail<=head  &&  r_i[dq[head]]<r_i[i]){
			head--;
		}
		dq[++head]=i;
		ans[i]=max(ans[i],r_i[dq[tail]]);
	}
}
void init(){
	log_n=0;
	while((1<<log_n)<=n){
		pow_2[log_n]=1<<log_n;
		log_n++;
	}
	log_n--;
	for(int i=0;i<=log_n;i++){
		solve(pow_2[i],pow_2[i]);
		for(int j=1;j<=n;j++){
			table[i][i][j]=ans[j];
		}
	}
	for(int i=1;i<=log_n;i++){
		solve(pow_2[i-1],pow_2[i]);
		for(int j=1;j<=n;j++){
			table[i-1][i][j]=ans[j];
		}
	}
	for(int dif=2;dif<=log_n;dif++){
		for(int l=0;l+dif<=log_n;l++){
			int r=l+dif;
			for(int i=1;i<=n;i++){
				table[l][r][i]=max(table[l][r-1][i],table[l+1][r][i]);
			}
		}
	}
}
unsigned long long query(int L,int R){
	if(2*L>=R){
		solve(L,R);
		for(int i=1;i<=n;i++){
			final_ans[i]=ans[i];
		}
	}
	else{
		int pow_id1;
		for(int i=log_n;i>=0;i--){
			if(pow_2[i]>=L){
				pow_id1=i;
			}
		}
		int pow_id2;
		for(int i=0;i<=log_n;i++){
			if(pow_2[i]<=R){
				pow_id2=i;
			}
		}
		for(int i=1;i<=n;i++){
			final_ans[i]=table[pow_id1][pow_id2][i];
		}
		solve(L,pow_2[pow_id1]);
		for(int i=1;i<=n;i++){
			final_ans[i]=max(final_ans[i],ans[i]);
		}
		solve(pow_2[pow_id2],R);
		for(int i=1;i<=n;i++){
			final_ans[i]=max(final_ans[i],ans[i]);
		}
	}
	unsigned long long output=0;
	for(int i=1;i<=n;i++){
		output^=(unsigned long long)(i)*final_ans[i];
	}
	return output;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	init();
	int q;
	scanf("%d",&q);
	while(q--){
		int L,R;
		scanf("%d %d",&L,&R);
		printf("%llu\n",query(L,R));
	}
	return 0;
}
posted @ 2025-11-29 20:31  Oken喵~  阅读(32)  评论(0)    收藏  举报