zoukankan      html  css  js  c++  java
  • Codeforces 1299C Water Balance

    Description

    描述

    给一个序列,每次可以将一个区间内的所有数都变成操作前这个区间的平均数,求最后能得到的字典序最小的结果。

    输入

    第一行一个正整数 $n$($1 le n le 10^6$)。

    第二行 $n$ 个正整数,表示序列 $a$($1 le a_i le 10^6$)。

    输出

    $n$ 行,表示最终序列。

    样例

    输入1

    4
    7 5 5 7

    输出1

    5.666666667
    5.666666667
    5.666666667
    7.000000000

    输入2

    5
    7 8 8 10 12

    输出2

    7.000000000
    8.000000000
    8.000000000
    10.000000000
    12.000000000

    输入3

    10
    3 9 5 5 1 7 5 3 8 7

    输出3

    3.000000000
    5.000000000
    5.000000000
    5.000000000
    5.000000000
    5.000000000
    5.000000000
    5.000000000
    7.500000000
    7.500000000

    解释

    样例1:对 $[1, 3]$ 操作。

    样例2:无法更优。

    Solution

    首先,$mathcal O(n^2)$ 的思路十分好想。

    大概就是说,我们发现答案序列是单调不降的,所以倒着来,对于每个位置 $i$,我们找一个它后面的位置 $j$,使得 $frac{sum_{k=i}^{j} a_k}{j-i+1}$ 是最小的,然后把这一段全部赋值为这个值。这样,我们就在优先确保前面的值更优时,得到了一个可靠的贪心策略。

    因为 $10^6$ 肯定要更好的方法,所以考虑优化。当处理位置 $i$ 的时候,$(i, n]$ 已经处理好了,成为了一个不降的,若干个连续段组成的数列。而对于连续的一段,我们 要不一起选,要不一起不选,所以就可以用一个栈来维护后面的这个东西。栈里存一个三元组,表示这一段的左端点 $l$,右端点 $r$,以及总和 $w$。

    比如这时,已选的数字的总和为 $sum$,已选的数字总个数为 $tot$,显然,如果处于栈顶的那一段(不妨叫做 $top$)能够 拖低平均值,即 $frac{w_{top}}{r_{top}-l_{top} +1} le frac{sum}{tot}$(注意等于也可以不加,反正不会改变什么),那么我们就要选这一段。细节问题是避免浮点计算,交叉相乘,看看 $w_{top} imes tot le sum imes (r_{top}-l_{top}+1)$ 就行了。

    时间复杂度 $mathcal O(n)$,下面是代码。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6 + 5;
    int n, l[N], r[N], top, a[N];
    long long w[N];
    double ans[N];
    int main()
    {
    	scanf("%d", &n);
    	for(register int i = 1; i <= n; i++) scanf("%d", &a[i]);
    	top = 1;
    	l[1] = r[1] = n;
    	w[1] = a[n];
    	l[0] = n + 1;
    	for(register int i = n - 1; i; i--)
    	{
    		int tot = 1;
    		long long sum = a[i];
    		while(top && w[top] * tot <= sum * (r[top] - l[top] + 1))
    		{
    			sum += w[top];
    			tot += r[top] - l[top] + 1;
    			top--;
    		}
    		top++;
    		r[top] = l[top - 1] - 1;
    		l[top] = i;
    		w[top] = sum;
    	}
    	for(register int i = 1; i <= top; i++)
    		for(register int j = l[i]; j <= r[i]; j++) ans[j] = 1.0 * w[i] / (r[i] - l[i] + 1);
    	for(register int i = 1; i <= n; i++) printf("%.10lf
    ", ans[i]);
    	return 0;
    }

    吐槽:

    • 为啥 $mathcal O(n^2)$ 能水过去?QwQ
    • 为啥全部开 $ exttt{long double}$ 会 TLE? QwQ
    • 为啥用 $ exttt{iostream}$ 会卡常?QwQ

    所以这是一道毒瘤题……

  • 相关阅读:
    Python基础---------数据类型
    Linux----------Rabbitmq消息队列
    常用消息中间件对比
    Linux-------------kafaka基础
    Linux------------zookeeper基础
    CentOS使用chkconfig增加开机服务提示service xxx does not support chkconfig的问题解决
    Linux-------------MongoDB基础
    Web Service
    AutoResetEvent和ManualResetEvent(多线程操作)
    方法的可变参数 params
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/CF1299C.html
Copyright © 2011-2022 走看看