P7482 不条理狂诗曲 分治 DP

P7482 不条理狂诗曲 分治 DP

题意

有一个长度为(n)的非负整数序列(a),定义(f(l,r))表示从序列(a)的区间([l,r])选择若干不相邻的数的和的最大值

现要求

[sum_{l=1}^nsum_{r=l}^nf(l,r) ]

取模(1e9 + 7)

[1 leq n leq 10^5\ 0 leq a_i leq 10^9 ]

分析

好久没做过这么单纯的分治了。。

先想想一个区间怎么做,那么显然跑一遍DP就行了

然后可以利用分治的思想去算每个区间

对于区间([l,r]),如果子区间在([l,mid])或者([mid + 1,r]),直接递归下去就行了

重点在于横跨中间点

考虑到不能相邻,用(fl_i)表示([i,mid])且选(mid)的最大和,(fr_i,gl_i,gr_i)同理,这些显然都可以跑一遍(DP)求出

那么对于区间([L,R]),答案应该是(max(fl_L + gr_R,gl_L + fr_R,gl_L + gr_R))

将下标移到一边可以推得(fr_R - gr_R > max(fl_L - gl_L,0))

于是各自维护几个变量,枚举(r),就可以二分算出一段的贡献

于是就可以把朴素的(n^2)优化到(nlogn)

总复杂度(O(nlog^2n))

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define pii pair<ll,int>
#define re register
#define all(x) x.begin(),x.end()
using namespace std;
typedef long long ll;

ll rd(){
	ll x = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		ch = getchar(); 
	}
	while(ch >= '0' && ch <= '9') {
		x  = x * 10 + ch - '0';
		ch = getchar(); 
	}
	return x;
}

const int maxn = 1e5 + 5;
const ll MOD = 1e9 + 7;

pii v[maxn];
int a[maxn];
ll ans;
ll fl[maxn],fr[maxn],gl[maxn],gr[maxn];
ll sum1[maxn],sum2[maxn];

//fl: choose mid
//gl: choose mid - 1
//fr: choose mid + 1
//gr: choose mid + 2

//for interval: [L,R]
//ans: max(fl_L + gr_R,gl_L + fr_R,gl_L + gr_R) = max(max(fl_L,gl_L) + gr_R,gl_L + fr_R)
// gl + fr > max(fl,gl) + gr
// fr_R - gr_R > max(fl_L - gl_L,0)
// g:max(fl_L - gl_L,0) 
// for every j: find the first pos  (fr_R - gr_R) >= (g) k, 
// l - k - 1:
// ans += sum1 + fr_R * (k - l)
// k - r :
// ans += (mid - k + 1) * gr_R + sum3[k - r]


void solve(int l,int r){
	if(l == r) {
		ans += a[l];
		ans %= MOD;
		return;
	}
	int mid = l + r >> 1;
	solve(l,mid);
	solve(mid + 1,r);
	for(re int i = mid;i >= l;i--){
		if(i == mid) fl[i] = a[i],fl[i + 1] = gl[i] = 0;
		else 
			fl[i] = max(fl[i + 1],fl[i + 2] + a[i]);
		if(i == mid - 1) 
			gl[i] = a[i],gl[i + 1] = 0;
		else if(i != mid ) 
			gl[i] = max(gl[i + 1],gl[i + 2] + a[i]);
		v[i] = make_pair(max(0ll,fl[i] - gl[i]),i);
	}
	sort(v + l,v + mid + 1);
	sum1[l - 1] = sum2[l - 1] = 0;
	for(re int i = l;i <= mid;i++){
		sum1[i] = sum1[i - 1] + max(fl[v[i].se],gl[v[i].se]);
		sum1[i] %= MOD;
		sum2[i] = sum2[i - 1] + gl[v[i].se];
		sum2[i] %= MOD; 
	}
	for(re int i = mid + 1;i <= r;i++){
		if(i == mid + 1) fr[i] = a[i],fr[i - 1] = gr[i] = 0;
		else 
			fr[i] = max(fr[i - 1],fr[i - 2] + a[i]);
		if(i == mid + 2) gr[i] = a[i];
		else if(i != mid + 1) gr[i] = max(gr[i - 1],gr[i - 2] + a[i]);
		int k = lower_bound(v + l,v + mid + 1,make_pair(fr[i] - gr[i],0)) - v;
		ans += sum1[mid] - sum1[k - 1] + (mid - k + 1) * gr[i] % MOD;
		ans %= MOD;
		ans += sum2[k - 1] + (k - l) * fr[i] % MOD;
		ans %= MOD;
	}
} 


int main(){
	int n = rd();
	for(int i = 1;i <= n;i++)
		a[i] = rd();
	solve(1,n);	
	cout << ans;
}
原文地址:https://www.cnblogs.com/hznumqf/p/14702491.html