zoukankan      html  css  js  c++  java
  • 【BZOJ 1911】【APIO 2010】特别行动队

    http://www.lydsy.com/JudgeOnline/problem.php?id=1911

    夏令营里斜率优化的例题,我调了一晚上,真是弱啊。

    先推公式吧($sum_i$表示$x_1 dots x_i$的和):

    $$①f(i)=f(j)+a(sum_i -sum_j)^2 +b(sum_i -sum_j)+c$$

    $$②f(i)=f(k)+a(sum_i -sum_k)^2 +b(sum_i -sum_k)+c$$

    ①和②分别表示从j和k这两个位置的转移过程,且满足$0≤j<k<i$

    然后假设②比①更优,则②的等号右边减去①的等号右边大于0

    $$f(k)-f(j)+a(sum_i -sum_k)^2 -a(sum_i -sum_j)^2 +b(sum_i -sum_k)  -b(sum_i -sum_j)>0$$

    把平方算出来后相同的项消去得到:

    $$f(k)-f(j)+2asum_i(sum_j - sum_k)-a(sum_j^2 - sum_k^2)+b(sum_j -sum_k)>0$$

    又因为$sum_j -sum_k < 0$,所以两边同时除以$sum_j -sum_k$:

    $$frac{f(k)-f(j)}{sum_j -sum_k}+2asum_i +b-a(sum_j + sum_k)<0$$

    移项后通分:

    $$2asum_i +b<frac{[f(k)+asum_k^2]-[f(j)+asum_j^2]}{sum_k -sum_j}$$

    $2asum_i +b$是单调递减的,这样就化成了一个斜率优化的式子,对于一个位置$t$,可以把它看成二维平面上坐标为$(sum_t,f(t)+asum_t^2)$的点,用双端队列维护一个这些点的下凸壳进行转移,时间复杂度$O(n)$

    夏令营讲题时自己推式子推错了!!!斯巴达!!!!!!!!!好久才发现。式子改正过来后对拍还是错,后来发现改的太急了忘加了两个括号斯巴达!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    为了A掉这道题耗了一晚上,全是脑残和手残造成的,已无力吐槽_(:з」∠)_

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 1000003;
    typedef long long ll;
    int in() {
    	int k = 0, fh = 1; char c = getchar();
    	for(; c < '0' || c > '9'; c = getchar())
    		if (c == '-') fh = -1;
    	for(; c >= '0' && c <= '9'; c = getchar())
    		k = (k << 3) + (k << 1) + c - '0';
    	return k * fh;
    }
    
    ll f[N], sum[N], key;
    int n, a, b, c, q[N];
    
    bool cmp(int x, int y) {
    	return f[y] + sum[y] * sum[y] * a  - f[x] - sum[x] * sum[x] * a > key * (sum[y] - sum[x]);
    }
    bool cmpk(int x, int y, int z) {
    	return (f[z] + sum[z] * sum[z] * a - f[y] - sum[y] * sum[y] * a) * (sum[y] - sum[x])
    		 > (f[y] + sum[y] * sum[y] * a - f[x] - sum[x] * sum[x] * a) * (sum[z] - sum[y]);
    }
    
    int main() {
    	n = in(); a = in(); b = in(); c = in();
    	sum[0] = 0;
    	for(int i = 1; i <= n; ++i)
    		sum[i] = in(), sum[i] += sum[i - 1];
    	
    	ll qu;
    	int head = 0, tail = 1, t;
    	f[0] = 0;
    	f[1] = sum[1] * sum[1] * a + sum[1] * b + c;
    	q[0] = 0; q[1] = 1;
    	for(int i = 2; i <= n; ++i) {
    		key = sum[i] * a * 2 + b;
    		while (head < tail && cmp(q[head], q[head + 1])) ++head;
    		t = q[head]; qu = sum[i] - sum[t];
    		f[i] = f[t] + qu * qu * a + qu * b + c;
    		while (head < tail && cmpk(q[tail - 1], q[tail], i)) --tail;
    		q[++tail] = i;
    	}
    	
    	printf("%lld
    ", f[n]);
    	return 0;
    }

    ( ̄▽ ̄")不过最后还是A掉了233

  • 相关阅读:
    C++中的模板编程
    C++中的抽象类
    C++中的多态
    C++中的继承
    操作符重载(二)
    操作符重载(一)
    C++中的类与对象模型
    [八省联考2018] 劈配 (网络流+二分)
    CF51F Caterpillar (边双+树形DP)
    CF36E Two Paths (欧拉回路+构造)
  • 原文地址:https://www.cnblogs.com/abclzr/p/5689837.html
Copyright © 2011-2022 走看看