zoukankan      html  css  js  c++  java
  • [NOIp2016] 蚯蚓

    类型:单调队列

    传送门:>Here<

    题意:有$N$只蚯蚓,每秒都会伸长$q$。每一次都会有人选出最长的一条切成两半,长度分别是$left lfloor px ight floor$和$x - left lfloor px ight floor$ 询问每一秒最长的蚯蚓被切前的长度,以及$m$秒后每条蚯蚓的长度(从大到小排序)

    解题思路

    NOIp的subtask还是非常良心的。于是决定不看题解开始干……

    看完题目,花了10分钟打了一个超级暴力模拟,35分get 然后发现有60分是$q=0$的情况……这不是就是一个裸的堆吗?打了15分钟,50分get(为什么只有50……) 然后想了很久没思路,瞟了一眼题解一眼就看到去减长度而不用管加,赶紧开始打调试近40分钟后85分get 然后就开始看题解打正解了……正解也调了20分钟左右 最终的AC代码再交一次竟然变成了90 又交一次变成了95 再交一次又100了……洛谷的评测机也不是很稳定啊,这题还是有点卡常

    先来看看85分怎么拿。85分的写法在思想上还是很重要的——蚯蚓每秒增长$q$,可以看做是被切的蚯蚓减去$q$。因此我们可以维护一个堆,这样堆的内部就不需要反复更新。但是细节要注意,选择切割的蚯蚓长度应该拿真实的长度来算

    然后来看正解。很容易发现,对于两条蚯蚓$x,y$,如果$x>y$,则$x$肯定会先被切掉。不妨设$x$被切掉以后变为${a_1, b_1}$,$y$被切后变为${a_2, b_2}$

    由于$y$肯定在$x$被切后若干秒被切,不妨设为$t$秒,则那时四条分出来的小蚯蚓的长度是可以表示的。我们希望能够证明到那时$a_1 > a_2, b_1 > b_2$。我们先来证明$a_1>a_2$,$b$也类似$$a_1 = a_1 + q*t =left lfloor px ight floor + q*t$$$$a_2 = left lfloor p(y+q*t) ight floor$$则$$a_1-a_2=left lfloor px ight floor + q*t-left lfloor p(y+q*t)  ight floor$$去掉向下取整符号并不会影响答案,因此$$a_1-a_2=p*x+q*t-p*y-p*q*t$$整理得$$a_1-a_2=p(x-y)+q*t(1-p)>0$$故$$a_1>a_2$$

    因此我们得出结论:一条蚯蚓如果比另一条蚯蚓早被切,那么它分出来的两条蚯蚓也永远比后分出来的两条蚯蚓要长

    于是我们可以考虑维护三个队列$q_1,q_2,q_3$,$q_1$中储存没被切过的蚯蚓,$q_2$中储存切出来的左半条,$q_3$表示右半条。要求队列单调不上升,每一次取出三个队列的队头中的最大值即为所有蚯蚓中最长的,切成两半后分别塞到$q_2,q_3$的队尾。由于刚才证明了后切的一定要短,所以插入队尾后单调性依然满足。故这样的做法是正确的

    Code

    cur=-INF而不能是-1,因为负数有可能减到很大

    /*By DennyQi 2018.8.15*/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    #define  r  read()
    #define  Max(a,b)  (((a)>(b)) ? (a) : (b))
    #define  Min(a,b)  (((a)<(b)) ? (a) : (b))
    using namespace std;
    typedef long long ll;
    const int MAXN = 7000010;
    const int MAXM = 27010;
    const int INF = 1061109567;
    inline int read(){
        int x = 0; int w = 1; register int c = getchar();
        while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
        if(c == '-') w = -1, c = getchar();
        while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar(); return x * w;
    }
    double p;
    int N,M,Q,U,V,T,_mx,pos,tmp,x,y,cur,top;
    int q[4][MAXN],h[4],t[4],a[MAXN];
    inline bool comp(const int& a, const int& b){ return a>b; }
    int main(){
        N=r,M=r,Q=r,U=r,V=r,T=r;
        p = (double)(U) / (double)(V);
        for(int i = 1; i <= N; ++i) a[i] = r;
        h[1] = h[2] = h[3] = 1;
        q[2][1] = q[3][1] = -INF;
        sort(a+1,a+N+1,comp);
        for(int i = 1; i <= N; ++i) q[1][++t[1]] = a[i];
        for(int _t = 1; _t <= M; ++_t){
            cur = -INF;
            for(int i = 1; i <= 3; ++i){
                if(h[i] > t[i]) continue;
                if(q[i][h[i]] > cur){ cur = q[i][h[i]]; pos = i; }    
            }
            if(_t % T == 0) printf("%d ", cur+(_t-1)*Q);
            ++h[pos];
            x = (cur+(_t-1)*Q) * p, y = (cur+(_t-1)*Q) - x;
            q[2][++t[2]] = x-_t*Q, q[3][++t[3]] = y-_t*Q;
        }
        puts("");
        for(int i = 1; i <= 3; ++i)
            for(int j = h[i]; j <= t[i]; ++j) a[++top] = q[i][j];
        sort(a+1,a+top+1,comp);
        for(int i = 1; i <= top; ++i)
            if(i % T == 0) printf("%d ", a[i]+M*Q);
        return 0;
    }
  • 相关阅读:
    BZOJ2303:[APIO2011]方格染色(并查集)
    BZOJ1116:[POI2008]CLO(并查集)
    BZOJ4011:[HNOI2015]落忆枫音(DP,拓扑排序)
    洛谷1387 最大正方形
    洛谷 P1858 多人背包
    vijos 1085 Sunnypig闯三角关
    vijos 1030 重叠的方框
    codevs 1001 舒适的路线 WK
    1266. [NOIP2012] 借教室
    codevs 2370 小机房的树
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/9481844.html
Copyright © 2011-2022 走看看