• 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环境维护
    哈希表解决字符串问题
    论文笔记二:《A Tutoral on Spectral Clustering》
    论文笔记之哈希学习比较--《Supervised Hashing with Kernels》《Towards Optimal Binary Code Learning via Ordinal Embedding》《Top Rank Supervised Binary Coding for Visual Search》
    Java中String、StringBuffer、StringBuilder的比较与源 代码分析
    浙大pat1040 Longest Symmetric String(25 分)
    浙大pat1039 Course List for Student(25 分)
    浙大pat---1036 Boys vs Girls (25)
    百炼oj-4151:电影节
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/CF1299C.html
Copyright © 2020-2023  润新知