zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Linux和windows 查看程序、进程的依赖库的方法
    ida pro 使用
    sql server相关
    nginx 日志log_format格式
    .yml文件格式
    svn加锁设置
    包含目录、库目录、附加包含目录、附加库目录、附加依赖项之详解
    visual studio 2017 (vs2017安装)
    OpenResty编译安装
    微信扫描二维码登录网站技术原理
  • 原文地址:https://www.cnblogs.com/hznumqf/p/14702491.html
Copyright © 2011-2022 走看看