Loading

P7482 不条理狂诗曲 分治 DP

P7482 不条理狂诗曲 分治 DP

题意

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

现要求

\[\sum_{l=1}^n\sum_{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;
}
posted @ 2021-04-25 22:40  MQFLLY  阅读(153)  评论(0编辑  收藏  举报