• 20171124校内训练


    把那个sigma符号看成枚举。

     显然,这个函数是单峰函数,且开口向上(自己脑补一下)

     然后三分。对于把所有数减去一个数X,如何统计此时的sum值呢?

    对于一个右端点,如何找到一个左端点使得此时的sum值最大?

    对于一个区间[x,y],我们可以由[1,y]-[1,x-1]来得到这个区间的值。

    如果我们已经知道了当前的前缀最小值和最大值,即对于一段区间[1,x],我们已知区间[1,i](1<=i<=x)的最小值和最大值,我们如何算出[j,x](1<=j<=x)的最小值和最大值?

    我们发现,这个值只有可能是从当前前缀和-前缀最小值(前缀最大值)得到的。

    因为减去前缀最小值,会让所得到的区间尽可能的大(即求出[j,x](1<=j<=x)的最大值)。

    减去前缀最大值,会让所得到的区间尽可能的小(即求出[j,x](1<=j<=x)的最小值)。

    然后就能求出答案。

    每次check要初始化Min(前缀最小值)=0,Max(前缀最大值)=0,Ans(sum值)=0,pre(当前前缀和)=0。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    double eps=0.00000001;
    double a[101000];int n;
    double Abs(double x){return x>0?x:-x;}
    double check(double x)
    {
        double Min=0,Max=0,Ans=0,pre=0;
        for(int i=1;i<=n;i++)
        {
            pre+=a[i]-x;Min=min(Min,pre);Max=max(Max,pre);
            Ans=max(Ans,max(Abs(pre-Min),Abs(pre-Max)));
        }
        return Ans;
    }
    int main()
    {
        freopen("array.in","r",stdin);freopen("array.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){int x;scanf("%d",&x);a[i]=(double)x;}
        double l=-100000.0,r=100000.0;
        for(int i=1;i<=100;i++)
        {
            double mid1=l+(r-l)/3,mid2=r-(r-l)/3;
            if(check(mid1)<check(mid2))r=mid2;
            else l=mid1;
        }
        printf("%.6lf",check(l));
        return 0;
    }
    View Code

     把搬砖的过程建成一棵树,根节点有所有的砖头,然后每次分成最多三个儿子。每个叶子节点可以看成每个工地。

    我们可以发现,最终的答案就是每个点的点权(工地的所需砖头数)乘以深度的最小值。(根节点的深度为0)

    我们想到了什么?三叉Huffman树!

    由于我们要让它的深度尽量小,所以我们每个节点要尽量分3叉或者不分。

    但是万一题目给出的数是偶数呢?补一个点权为0的节点,照常构造三叉Huffman树。(其实这棵树不用真正的建出来,我们只需要计算答案就行了)

    还有一种思考方式:考虑到分砖头太难,我们考虑合并砖头。则题意变为:有n堆砖头,我们要把这些砖头合并成一堆。每次最多合并3堆砖头。合并的代价为你合并的砖头总数。求最小代价。

    合并果子!这就比较好做,贪心,每次合并最小3堆。可是,偶数怎么办呢?

    偶数就意味着你最后合并完了后只剩两堆,但这样合并一般不优啊。所以,我们就考虑先合并最小的两堆,然后3堆3堆的合。

    至于为什么要合并最小的两堆,我们可以看成,这里还有一堆0个砖头,这样我们就要把这三堆合并。即先合并最小的两堆。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    using namespace std;
    long long ans=0;
    priority_queue<long long,vector<long long>,greater<long long> > q;
    int main()
    {
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++){long long x;scanf("%lld",&x);q.push(x);}
        if(n==1){printf("%lld",q.top());return 0;}
        if(n%2==0){long long x_1=q.top();q.pop();long long x_2=q.top();q.pop();q.push(x_1+x_2);ans+=x_1+x_2;}
        while(q.size()>1)
        {
            long long x[3];x[1]=q.top();q.pop();x[2]=q.top();q.pop();x[3]=q.top();q.pop();
            ans+=x[1]+x[2]+x[3];q.push(x[1]+x[2]+x[3]); 
        }
        printf("%lld",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    Codeforces Round #649 (Div. 2) A、B、C、
    poj1061 青蛙的约会(扩展欧几里得)
    Educational Codeforces Round 89 (Rated for Div. 2)A、B、C、D、E
    jxust摸底测试1
    Codeforces Round #648 (Div. 2) A、B、C、D、E、F
    大数模板
    任意进制转换(2019 ICPC Asia Yinchuan Regional Base62)
    求素数(从判断素数到筛法)
    直线 (__int128的使用)
    E. Tree Shuffling (Round #646 (Div. 2))
  • 原文地址:https://www.cnblogs.com/lher/p/7892355.html
Copyright © 2020-2023  润新知