zoukankan      html  css  js  c++  java
  • NOIP 模拟赛 左右横跳

    ( ext{Problem})

    大意就是优化这样一个 (dp)

    [f_{i}=max f[j]+(i-j) cdot (i-j-1) ]

    (L[i] le j < i,nle 5 imes 10^6)
    (L[i]) 给出且满足 (L[x] le L[x+1])

    ( ext{Solution})

    本做法经大佬指点
    (O(n log n)) 时限有 (2.5s)(log) 来源于树状数组是可以过的
    当然本题存在线性做法 (然而没懂)

    显然斜率优化,最大值维护上凸包
    然而你会发现 (L) 的限制很可恨,对于入队的点,维护凸包时弹掉的点可能是以后的最优决策(因为(L)可以让你取不到没限制时的最优点,不得不往后选在 (L) 范围内的点,而这些点可能被维护凸包时弹掉了)
    但要明确一点,如果你把可以用的决策点合成一块后维护上凸包,就可以用常规斜率优化弹点寻找最优点
    那么我们如何快速把 ([L[i],i)) 的所有决策提出来维护凸包?
    再明确一件事,把 ([L[i],i)) 分成连续的几块,对每块的最优值取最大值是等价于整块的最优值的(显然)
    那么如何优秀地分块
    注意到树状数组本身就是个前缀和,且拆成了 (log) 块,可以让树状数组上每个点 (x) 维护一个区间的凸包(每个点维护个单调栈,开 ( ext{vector})
    本题维护后缀和

    这样就可以成功了

    ( ext{Code})

    #include <cstdio> 
    #include <vector>
    #include <iostream>
    #define LL long long
    #define re register 
    using namespace std;
    
    const int N = 5e6 + 5;
    int n, L[N];
    LL f[N];
    
    inline int read(int &x)
    {
    	x = 0; char ch = getchar();
    	while (!isdigit(ch)) ch = getchar();
    	while (isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar();
    }
    
    inline double slope(int j, int k)
    {
    	return 1.0 * (f[j] + 1LL * j * j + j - f[k] - 1LL * k * k - k) / (j - k);
    }
    struct Stack{
    	vector<int> Q;
    	inline int size(){return Q.size();}
    	inline int top1(){return Q[Q.size() - 1];}
    	inline int top2(){return Q[Q.size() - 2];}
    	inline void pop(){Q.pop_back();}
    	inline void push(int x){Q.push_back(x);}
    };
    struct BIT{
    	Stack t[N];
    	inline int lowbit(int x){return x & (-x);}
    	inline LL calc(int i, int j)
    	{
    		return f[j] + 1LL * (i - j) * (i - j - 1);
    	}
    	inline LL query(int x, int k)
    	{
    		LL res = 0;
    		for(; x <= n; x += lowbit(x))
    		{
    			while (t[x].size() > 1 && slope(t[x].top2(), t[x].top1()) < k) t[x].pop();
    			if (t[x].size()) res = max(res, calc(k / 2, t[x].top1()));
    		}
    		return res;
    	}
    	inline void insert(int i)
    	{
    		for(re int x = i; x; x -= lowbit(x))
    		{
    			while (t[x].size() > 1 && slope(t[x].top2(), t[x].top1()) < slope(t[x].top1(), i)) t[x].pop();
    			t[x].push(i);
    		}
    	}
    }T;
    
    int main()
    {
    	freopen("jump.in", "r", stdin), freopen("jump.out", "w", stdout);
    	read(n);
    	for(re int i = 2; i <= n + 1; i++) read(L[i]), ++L[i];
    	T.insert(1);
    	for(re int i = 2; i <= n + 1; i++) f[i] = T.query(L[i], 2 * i), T.insert(i);
    	printf("%lld
    ", f[n + 1]);
    }
    
  • 相关阅读:
    创建线程的方式三:实现Callable接口 --- JDK 5.0新增
    线程的通信
    多线程的实例练习:银行账户双储户问题
    解决线程安全问题的方式三:Lock锁 --- JDK5.0新增
    演示线程的死锁问题
    Synchronized的各场景使用方法(多窗口售票例子接上篇)
    线程的【生命周期】和【线程的同步】(下面有多窗口售票例子)
    多线程:继承方式和实现方式的联系与区别
    创建多线程的方式二:实现Runnable接口
    Java项目生成可执行jar包、exe文件以及在Windows下的安装文件
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/15412589.html
Copyright © 2011-2022 走看看