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;
    }
    
  • 相关阅读:
    15. DML, DDL, LOGON 触发器
    5. 跟踪标记 (Trace Flag) 834, 845 对内存页行为的影响
    4. 跟踪标记 (Trace Flag) 610 对索引组织表(IOT)最小化日志
    14. 类似正则表达式的字符处理问题
    01. SELECT显示和PRINT打印超长的字符
    3. 跟踪标记 (Trace Flag) 1204, 1222 抓取死锁信息
    2. 跟踪标记 (Trace Flag) 3604, 3605 输出DBCC命令结果
    1. 跟踪标记 (Trace Flag) 1117, 1118 文件增长及空间分配方式
    0. 跟踪标记 (Trace Flag) 简介
    SpringBoot + Redis + Shiro 实现权限管理(转)
  • 原文地址:https://www.cnblogs.com/hznumqf/p/14702491.html
Copyright © 2011-2022 走看看