[LOJ#6490]「XXOI 2018」暑假时在做什么?有没有空?可以来学物理吗? 题解
题意:给定 \(a_{1\sim n},L,R\),\(\forall i \in [1,n]\cap Z\),求
\[f_i=\max\limits_{1\le x\le i\le y\mathop{且}L\le y-x+1\le R} \sum\limits_{k=x}^y a_k \]\(1\le n,L,R \le 10^5,1\le |a_i|\le 10^5\)。
Preface
被这个标题吸引进来。
BDFS 了一圈也没有找到这道题的解法,只能对着别的大佬的代码硬啃。
这可能是这道题的第一篇题解(叭),我会尽我所能解释这道题的做法。
(当然,可能会因此显得啰嗦)
Analysis
首先,最基础的前缀和转化以及 ST 表求区间极值不再赘述。
发现这题通过普通的枚举手段肯定没法快速计算,况且题中 \(a\) 含负数,不具有单调性。
在枚举遇到瓶颈的时候,大概率是有大量的无用信息,或者有很多有用的信息无法使用。
这时可以尝试往分治想,考虑如何在这道题上使用分治。
假设当前的子问题为 \(solve(l,r)\),如果 \(l=r\) 且 \(L=1\),显然有 \(f_l=a_l\)。
否则,令 \(mid=\frac{l+r}{2}\),递归处理 \(solve(l,mid),solve(mid+1,r)\)。
接着计算两部分之间互相的影响。
以 \([l,mid]\) 的部分为例,我们开一个变量 \(pre\) 记录最大值。
记 \(x=\max(i+L-1,mid+1),y=\min(i+R-1,r)\)。
每扫到一个 \(i\),令 \(pre=\max(pre,\max\limits_{k=x}^y\{sum_k\}-sum_{i-1}),f_i=\max(f_i,pre)\)。
正确性仔细想想,其实并不复杂。
通过 \(x,y\) 的范围,我们强制让右端点在 \([mid+1,r]\),这样 \([i,mid]\) 中的所有数都会被覆盖到,显然不会造成错误的更新。
根据分治本身的正确性,肯定不会有任何一种最优解被漏算。
(感觉解释的有点浅,有问题的话我再改改,其实仔细想想是能想明白的)
时间复杂度:\(O(n\log n)\)。
Code
// Problem: #6490. 「XXOI 2018」暑假时在做什么?有没有空?可以来学物理吗?
// Contest: LibreOJ
// URL: https://loj.ac/p/6490
// Memory Limit: 256 MB
// Time Limit: 400 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
typedef long long ll;
const ll INF = 1e18;
const int maxn = 1e5 + 5;
int n,L,R,a[maxn];
ll sum[maxn],f[maxn];
namespace ST {
int lg[maxn];
ll f[maxn][20],dp[maxn][20];
void init() {
for(int i = 0;i <= n;++ i)f[i][0] = dp[i][0] = sum[i];
for(int i = 2;i <= n + 1;++ i)lg[i] = lg[i >> 1] + 1;
for(int k = 1;(1 << k) <= n;++ k) {
for(int i = 0;i + (1 << k) - 1 <= n;++ i) {
f[i][k] = std::max(f[i][k - 1] , f[i + (1 << (k - 1))][k - 1]);
dp[i][k] = std::min(dp[i][k - 1] , dp[i + (1 << (k - 1))][k - 1]);
}
}
return ;
}
ll RMQmax(int l,int r) {
if(l > r)return -INF;
int k = lg[r - l + 1];
return std::max(f[l][k] , f[r - (1 << k) + 1][k]);
}
ll RMQmin(int l,int r) {
if(l > r)return INF;
int k = lg[r - l + 1];
return std::min(dp[l][k] , dp[r - (1 << k) + 1][k]);
}
}
void solve(int l,int r) {
if(r - l + 1 < L)return ;
if(l == r) {
if(L == 1)f[l] = std::max(f[l] , (ll)a[l]);
return ;
}
int mid = (l + r) >> 1;
solve(l , mid);
solve(mid + 1 , r);
ll pre = -INF,suf = -INF;
for(int i = l;i <= mid;++ i) {
int x = i + L - 1,y = i + R - 1;
pre = std::max(pre , ST::RMQmax(std::max(x , mid + 1) , std::min(y , r)) - sum[i - 1]);
f[i] = std::max(f[i] , pre);
}
for(int i = r;i > mid;-- i) {
int x = i - R + 1,y = i - L + 1;
suf = std::max(suf , sum[i] - ST::RMQmin(std::max(x , l) - 1 , std::min(y , mid) - 1));
f[i] = std::max(f[i] , suf);
}
return ;
}
int main() {
scanf("%d %d %d",&n,&L,&R);
for(int i = 1;i <= n;++ i)
scanf("%lld",&a[i]),sum[i] = sum[i - 1] + a[i],f[i] = -INF;
ST::init();
solve(1 , n);
for(int i = 1;i <= n;++ i)printf("%lld ",f[i]);
return 0;
}