zoukankan      html  css  js  c++  java
  • NOIP 2016 蚯蚓

    题目链接https://www.luogu.com.cn/problem/P2827

    不知道为啥出题人跟蚯蚓过不去

    分析

    这道题我看到后第一感觉是模拟,因为它的输出很有特点,既有过程也有最终答案,所以不可能会有什么公式之类的,那怎么模拟呢?这个蚯蚓长度是动态更新的,所以我们可以考虑动态维护它的大小,用一个优先队列就行,当然,最开始我觉得能过,因为某OJ上边写的是十秒,(其实后来算了算十秒也是过不了的),好吧你不信的话现在来算一算,二叉堆的时间复杂度是(O(NlogN)),放到这题就是(MlogN),最大值是多少呢?(7*10^6*log_210^5)(log_210^5)大概是17左右,7*17是119,总的时间复杂度最低也是(1.19*10^8),然而因为(STL)自带大常数,所以最多跑到(10^7)就会T,故(10s)也不够用,那手写堆呢?感觉应该也不行,因为上述只是最低时间复杂度,事实上因为还有输入输出啥的,尤其这题输入输出比较多,也会T掉。

    我们先不考虑T掉的问题,这个可以优化,先想想暴力怎么做。其实也很简单,就每次从堆里揪出一只蚯蚓来劈成两半然后再加进去就行,那蚯蚓的自然生长怎么办??也很简单,只有这一只蚯蚓不需要生长,所以我定义一个(sum)变量记录蚯蚓的生长,每次让它加上生长长度,把蚯蚓劈完后减去生长长度再扔进去就行,这样就等价于它没有生长。代码如下TLE

    #include<cstdio>
    #include<queue>
    #include<cmath>
    using namespace std;
    priority_queue<int > que;
    int main(){
        int n,m,q,u,v,t;
        int sum=0;
        scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
        for(int i=1;i<=n;i++){
            int a;
            scanf("%d",&a);
            que.push(a);
        }
        double p=(double)u/v;
        for(int i=1;i<=m;i++){
            if(i%t==0)
                printf("%d ",que.top()+sum);
            u=que.top()+sum;que.pop();
            int now=(double)u*p;que.push(now-q-sum);que.push(u-now-sum-q);
            sum+=q;
        }
        printf("
    ");
        int cnt=0;
        while(!que.empty()){
            cnt++;
            if(cnt%t==0)printf("%d ",que.top()+sum);
            que.pop();
        }
    }
    

    优化

    优化说起来也很简单,就是不太好想到。对于这种需要重复排序的问题,很可能存在某种单调性,可以使(O(logN))的维护最大值变成(O(1))的,这道题就是。

    存在什么单调性呢?假设最开始的每只蚯蚓排序后从大到小依次长(x_i),那么我根据题意肯定是从(x_1)开始切,切完之后,假设(lfloor px floor)(l),另一半为(r),那么一定会从(l_1)开始单调递减,(r)同理,也就是说如果我建三个队列来分别维护初始,(l)(r),这三个队列都是单调的,即每次查询比较队首即可。下面来证明一下:

    先证明(l)是单调的,假设(i<j),设(c)表示蚯蚓增加的长度,根据初始的单调性,(x_i>=x_j),所以(lfloor px_i floor>=lfloor px_j floor)

    继而有(lfloor px_i+c floor>=lfloor p(x_j+c) floor)因为(p)小于1,所以(c>pc)

    因为在下取整号里边的是可以拿出来的,这一点是显然的,拿出来不会改变不等号方向。所以(lfloor px_i floor+c>=lfloor p(x_j+c) floor)

    易得(lfloor px_i floor>=lfloor p(x_j+c) floor-c),这时你会发现不等号左边就是(l_i)右边是(l_j),故原式得证。
    接下来证明(r)是单调的,其实两者差不多,都用到了放缩的思想。

    因为(x_i>=x_j)所以((1-p)x_i>=(1-p)x_j即x_i-px_i>=x_j-px_j)
    得,(lceil x_i-px_i ceil>=lceil x_j-px_j ceil)注意这里是上取整

    上取整号拆开需要稍微作一下变化,即(x_i-lfloor px_i floor>=x_j-lfloor px_j floor)

    然后可以得到,(x_i-lfloor px_i floor>=x_j+c-lfloor px_j floor-c)

    到这里会发现这个式子很接近于(r)了,只差最后一步放缩,因为减去一个正数不会让一个数更大,所以我在右边减去一个数不会让不等号方向改变
    于是可得(x_i-lfloor px_i floor>=x_j+c-lfloor p(x_j-c) floor-c)

    故有(r_i>=r_j),证毕。

    细节

    1.longlong用不用

    肯定是不需要的,假设最开始都是最长(10^8),每秒长(200),最多也就长(7*10^6*200)(14*10^8),二者相加是(15*10^8)
    (int)(21*10^8),所以不会爆。

    2.最大值的取法

    因为上边说了,最大可到(15*10^8),所以初始化INF不能用(0x3f3f3f3f),把3换成7就行。

    3.还跟longlong有关系

    那为啥有的代码没用longlong就错,用了就对呢?主要是一个地方

    这俩相乘的时候可能会爆longlong,当然你预处理那个分数也就自行忽视。

    #include<cstdio>
    #include<algorithm>
    const int N=7e6+10,INF=0x7f7f7f7f;
    using namespace std;
    int q1[N],tt1,hh1,q2[N],tt2=-1,hh2,q3[N],tt3=-1,hh3;
    int sum;
    bool cmp(int a,int b){
        return a>b;
    }
    int find(){
        int x=-INF;
        if(hh1<=tt1)x=max(x,q1[hh1]);
        if(hh2<=tt2)x=max(x,q2[hh2]);
        if(hh3<=tt3)x=max(x,q3[hh3]);
        if(hh1<=tt1&&x==q1[hh1])hh1++;
        else if(hh2<=tt2&&x==q2[hh2])hh2++;
        else if(hh3<=tt3&&x==q3[hh3])hh3++;
        return x;
    }
    int main(){
        int n,m,q,u,v,t;
        scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
        for(int i=0;i<n;i++)
            scanf("%d",&q1[i]);
        tt1=n-1;
        sort(q1,q1+n,cmp);
        for(int i=1;i<=m;i++){
            int x=find();
            x+=sum;
            int l=x*1ll*u/v;
            int r=x-l;
            sum+=q;
            q2[++tt2]=l-sum;
            q3[++tt3]=r-sum;
            if(i%t==0)printf("%d ",x);
        }
        printf("
    ");
        for(int i=1;i<=n+m;i++){
            int x=find();
            if(i%t==0)printf("%d ",x+sum);
        }
        return 0;
    }
    
  • 相关阅读:
    [转]ASP.NET中JSON的序列化和反序列化
    [转]JavaScriptSerializer中日期序列化
    [转]国外英语教学网页
    [转]linq to sql (Group By/Having/Count/Sum/Min/Max/Avg操作符)
    [转]Business Model Canvas(商业模式画布):创业公司做头脑风暴和可行性测试的一大利器
    [转]sql server transaction
    CentOS7安装配置PostgreSQL9.6
    使用struts的同步令牌避免form的重复提交
    Flink初探-为什么选择Flink
    jdbc三种常见用法
  • 原文地址:https://www.cnblogs.com/anyixing-fly/p/12716443.html
Copyright © 2011-2022 走看看