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