zoukankan      html  css  js  c++  java
  • P4983 忘情

    题目描述

    你的(npy)为了恶心你,特地请了四位大神和一个辣鸡!

    ( m hdxrie)说:“我们得求和。”于是有了(sumlimits_{i=1}^{n}x_i)

    ( m Imagine)说:“我们得有平均数。”于是有了(ar x)

    ( m TimeTraveller)说:“我们得有加减乘除。”于是有了一些恶心的组合。

    ( m Althen·Way·Satan)说:“我们还得有平方。”于是我们将它平方。

    最垃圾的( m ZredXNy)说:“那我帮你们整合一下。”

    于是,我们得到了这么一个式子(:)

    (frac{left((sumlimits_{i=1}^{n}x_i×ar x)+ar x ight)^2}{ar x^2})

    我们定义一段序列的值为这个,其中(n)为此序列的元素个数。

    我们给定一段长度为(n)的序列,现在要求将它分成(m)段,要求每一段的值的总和最小,求出这个最小值。

    题解

    这道题神似P5308 [COCI2019] Quiz。

    首先看到分成(k)段,很自然地可以想到(WQS)二分。

    (frac{left((sumlimits_{i=1}^{n}x_i×ar x)+ar x ight)^2}{ar x^2})化简一下可得等于((sum_{i = 1} ^ {n}x_i + 1) ^ 2)

    (f_i)表示到第(i)个数的最小价值和,很容易得到转移方程(f_i = min(f_i, f_j + (s_i - s_j + 1) ^ 2))

    假设(k < j < i)(j)(k)优,可以化简得到(frac{f_j + s_j ^ 2 - (f_k + s_k ^ 2)}{s_j - s_k} <= 2 * (s_i + 1))

    然后就(WQS)二分就好。

    #include <iostream>
    #include <cstdio>
    #define ll long long
    using namespace std;
    const int N = 1e5 + 5;
    const int ll inf = 1e15;
    int n, m, a[N], g[N], q[N];
    ll ans, f[N], s[N];
    inline int read()
    {
    	int x = 0, f = 1; char ch = getchar();
    	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
    	return x * f;
    }
    ll Y(int x) {return f[x] + s[x] * s[x];}
    ll X(int x) {return s[x];}
    double slope(int i, int j) {return (double)(Y(i) - Y(j)) / (double)(X(i) - X(j));}
    int check(ll mid)
    {
    	int l, r; q[l = r = 1] = 0;
    	for(int i = 1; i <= n; i ++)
    	{
    		while(l < r && slope(q[l], q[l + 1]) <= 2.0 * (double)(s[i] + 1.0)) l ++;
    		f[i] = f[q[l]] + (s[i] - s[q[l]] + 1) * (s[i] - s[q[l]] + 1) + mid; g[i] = g[q[l]] + 1;// + mid
    		while(l < r && slope(q[r], q[r - 1]) >= slope(q[r - 1], i)) r --;
    		q[++ r] = i;
    	}
    	return g[n] >= m;
    }
    void work()
    {
    	n = read(); m = read();
    	for(int i = 1; i <= n; i ++) {a[i] = read(); s[i] = s[i - 1] + a[i];}
    	ll l = -inf, r = inf;
    	while(l <= r)
    	{
    		ll mid = (l + r) >> 1;
    		if(check(mid)) ans = mid, l = mid + 1;
    		else r = mid - 1;
    	}
    	check(ans); printf("%lld
    ", f[n] - ans * m);
    }
    int main() {return work(), 0;}
    
  • 相关阅读:
    迭代器和生成器
    装饰器进阶
    闭包和装饰器
    函数的嵌套
    函数的参数
    什么是粘包现象
    模拟ssh远程命令执行
    客户端与服务端代码bug修复和加入循环
    用socket实现简单的套接字通讯
    网络编程之客户端和服务端,
  • 原文地址:https://www.cnblogs.com/Sunny-r/p/12625121.html
Copyright © 2011-2022 走看看