NOIP2016 蚯蚓

题目链接:https://www.luogu.org/problemnew/show/P2827

这道题当时第一眼看到的时候觉得好像很简单……?!以为直接用个优先队列维护最大值,再开个全局变量记录一下所有蚯蚓一共都长了多少就行了。

一看数据范围,7*10^6?!好像O(nlogn)跑不过只能拿80分……那我们就得考虑直接用线性算法来解决了。

我们使用优先队列来维护最大值,不过如果蚯蚓每次在切割之后长度要是能保持单调的话,我们就可以直接用队列模拟,就变成线性算法了。

如何证明这一点呢?

假设有两条蚯蚓长度分别为a,b ,a > b,那么由a切出的蚯蚓的长度为pa + q,(1-p)a + q,而由b切出的蚯蚓的长度却为pb + q,(1-p)b + q;显然由a切出的两部分蚯蚓的长度,要长于由b切出的两部分蚯蚓的长度。

也就是说,其实蚯蚓的长度本身就是具有单调性的,并不需要用堆去维护。如果一排蚯蚓按从大到小排好的话,那么是一定可以保证这排蚯蚓所切出的两部分蚯蚓的长度也是单调递减的。这样我们就可以开三个队列,第一个存没有被切过的蚯蚓,第二个存被切出来的长度为floor(px)的蚯蚓,第三个存长度为x-floor(px)的蚯蚓。之后在每次取值的时候取三个队列的最大值即可。注意始终要维护一个全局变量(随时间递增的)以记录蚯蚓一共长了多少,因为我们不能每次都给所有蚯蚓加长度。然后每次在取最大值的时候需要加上这个偏移量,因为你不能对负数向下取整……之后把计算之后的长度值减去偏移量再压入队列。(虽然队列中可能很多负数不过并没有关系,因为我们实际运算的时候都是用正数的)

还有就是这题好像不需要考虑什么精度问题,不要用浮点数保留p,直接在计算的时候取len*u/v即可,另一端长度用len-这段长度即可,反正肯定能过。(都向下取整了为啥要用浮点)

顺便说一下,这题要开longlong,而且在找最大值的时候,最大值得初值要设的足够小。

最后上一下代码。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

typedef long long ll;
using namespace std;
const int M = 100005;
const ll INF = 10000000000000000;

ll n,m,Q,u,v,t,a[M],x,y,z,add,maxn,pos;
queue<ll> q[5];
ll read()
{
    ll ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
    }
    return ans * op;
}
bool cmp(ll x,ll y)
{
    return x > y;
}

int main()
{
    n = read(),m = read(),Q = read(),u = read(),v = read(),t = read();
    rep(i,1,n) a[i] = read();sort(a+1,a+1+n,cmp);
    rep(i,1,n) q[1].push(a[i]);//按从大到小压入队列
    rep(i,1,m)
    {
        maxn = -INF;
        rep(j,1,3)
        {
            if(q[j].empty()) continue;
            if(q[j].front() > maxn) maxn = q[j].front(),pos = j;
        }//寻找最大元素
        q[pos].pop();//把元素删了(蚯蚓切了)
        ll now = maxn + add;
        if(!(i % t)) printf("%lld ",now);//符合要求就输出
        ll cur1 = now * u / v,cur2 = now - cur1;
        add += Q;
        q[2].push(cur1 - add),q[3].push(cur2 - add);//计算结束后压入队列
    }
    enter;
    rep(i,1,n+m) 
    {
        maxn = -INF,pos = 0;
        rep(j,1,3)
        {
            if(q[j].empty()) continue;
            if(q[j].front() > maxn) maxn = q[j].front(),pos = j;
        }
        if(!(i % t))printf("%lld ",maxn + add);
        q[pos].pop();//和上面操作基本一样
    }
    return 0;
}
/*
3 7 1 1 3 1
3 3 2
*/

 

posted @ 2018-08-05 23:26  CaptainLi  阅读(328)  评论(0编辑  收藏  举报