[LOJ#6490]「XXOI 2018」暑假时在做什么?有没有空?可以来学物理吗? 题解

传送门QAQ

题意:给定 \(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;
}
posted @ 2022-09-03 20:43  ImALAS  阅读(699)  评论(2编辑  收藏  举报