[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;
}

浙公网安备 33010602011771号