洛谷P2827 [NOIP2016]蚯蚓【单调性优化】

Time Limit: 10 Sec Memory Limit: 512 MB

Description

本题中,我们将用符号[c]表示对c向下取整,例如:[3.0」= [3.1」=[3.9」=3。蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓。

蛐蛐国里现在共有n只蚯蚓(n为正整数)。每只蚯蚓拥有长度,我们设第i只蚯蚓的长度为a_i(i=1,2,…,n),并保证所有的长度都是非负整数(即:可能存在长度为0的蚯蚓)。

每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半。

神刀手切开蚯蚓的位置由常数p(是满足0<p<1的有理数)决定,设这只蚯蚓长度为x,神刀手会将其切成两只长度分别为[px]和x-[px]的蚯蚓。特殊地,如果这两个数的其中一个等于0,则这个长度为0的蚯蚓也会被保留。

此外,除了刚刚产生的两只新蚯蚓,其余蚯蚓的长度都会增加q(是一个非负整常数)。

蛐蛐国王知道这样不是长久之计,因为蚯蚓不仅会越来越多,还会越来越长。蛐蛐国王决定求助于一位有着洪荒之力的神秘人物,但是救兵还需要m秒才能到来…(m为非负整数)蛐蛐国王希望知道这m秒内的战况。

具体来说,他希望知道:?m秒内,每一秒被切断的蚯蚓被切断前的长度(有m个数)?m秒后,所有蚯蚓的长度(有n+m个数)。蛐蛐国王当然知道怎么做啦!但是他想考考你…

Input

第一行包含六个整数n,m,q,u,v,t,其中:n,m,q的意义见问题描述;
u,v,t均为正整数;你需要自己计算p=u/v(保证0<u<v)t是输出参数,其含义将会在输出格式中解释。
第二行包含n个非负整数,为ai,a2,…,an,即初始时n只蚯蚓的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。
保证 1 &lt; = n &lt; = 1 0 5 , 0 &lt; m &lt; 7 ∗ 1 0 6 , 0 &lt; u &lt; v &lt; 1 0 9 , 0 &lt; = q &lt; = 200 , 1 &lt; t &lt; 71 , 0 &lt; a i &lt; 1 0 8 1&lt;=n&lt;=10^5,0&lt;m&lt;7*10^6,0&lt;u&lt;v&lt;10^9,0&lt;=q&lt;=200,1&lt;t&lt;71,0&lt;ai&lt;10^8 1<=n<=1050<m<71060<u<v<1090<=q<=2001<t<710<ai<108

Output

第一行输出[m/t]个整数,按时间顺序,依次输出第t秒,第2t秒,第3t秒……被切断蚯蚓(在被切断前)的长度。
第二行输出[(n+m)/t]个整数,输出m秒后蚯蚓的长度;需要按从大到小的顺序
依次输出排名第t,第2t,第3t……的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。即使某一行没有任何数需要 输出,你也应输出一个空行。


题目分析

首先朴素的思路是用维护所有蚯蚓
但是每次所有蚯蚓增加 q q q的操作不好实现

我们可以用一个全局变量 s u m sum sum记录当前总共增加了几次 q q q
设当前取出蚯蚓长度为 x x x x + s u m x+sum x+sum即为他的真正长度
为了保证取出的蚯蚓这一次不增加,我们插回去的两个蚯蚓长度要减 q q q
同时为了保证排序的正确性,插回去之前还要减 s u m sum sum

for(int i=1;i<=m;++i)
{
    int x=que.top()+sum; que.pop();
    if(i%t==0) printf("%d ",x);//第一个询问
    
    int v1=floor((dd)x*p),v2=(dd)x-v1;
    v1-=sum+q; v2-=sum+q;
    
    que.push(v1); que.push(v2);
    sum+=q;
}

上述思路复杂度约为 O ( ( n + m ) l o g ( n + m ) ) O((n+m)log(n+m)) O((n+m)log(n+m)),期望得分为 60 p t s 60pts 60pts~ 85 85 85

现在仔细观察被切蚯蚓长度的变化,你会发现其实它是满足单调性
假设当前有 x , y x,y x,y两个长度的蚯蚓
先切 x x x ⌊ p x ⌋ , x − ⌊ p x ⌋ \lfloor{px}\rfloor,x-\lfloor{px}\rfloor px,xpx,同时 y y y变成 y + q y+q y+q
再切 y + q y+q y+q ⌊ p y + p q ⌋ , y + q − ⌊ p y + p q ⌋ \lfloor{py+pq}\rfloor,y+q-\lfloor{py+pq}\rfloor py+pq,y+qpy+pq,同时上面被切过的两个变为 ⌊ p x ⌋ + q , x − ⌊ p x ⌋ + q \lfloor{px}\rfloor+q,x-\lfloor{px}\rfloor+q px+q,xpx+q
由于 x &gt; y , 0 &lt; p &lt; 1 x&gt;y,0&lt;p&lt;1 x>y,0<p<1,显然 ⌊ p x ⌋ + q &gt; ⌊ p y + p q ⌋ , x − ⌊ p x ⌋ + q &gt; y + q − ⌊ p y + p q ⌋ \lfloor{px}\rfloor+q&gt;\lfloor{py+pq}\rfloor,x-\lfloor{px}\rfloor+q&gt;y+q-\lfloor{py+pq}\rfloor px+q>py+pq,xpx+q>y+qpy+pq

也就是说我们并不需要刻意维护蚯蚓间的大小关系,因为它本身已经满足单调
只需要用三个队列分别维护还没切的被切开得到的第一部分被切开得到的第二部分
每次取出三个队列队首比较选择最大的,操作后放回对应队列队尾即可

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long lt;
typedef double dd;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=8e6;
int n,m,q,t,rt;
int sum;
dd u,v,p;
int que[3][maxn],ll[3],rr[3];
bool cmp(int a,int b){return a>b;}

int qmax()
{
    int res=-1e9,k;
    for(int i=0;i<3;++i)
    {
        if(ll[i]<rr[i]&&res<que[i][ll[i]])
        res=que[i][ll[i]],k=i;
    }
    ll[k]++; return res;
}

int main()
{
    n=read();m=read();q=read();
    scanf("%lf%lf",&u,&v); t=read(); p=u/v;
    
    for(int i=0;i<3;++i) ll[i]=rr[i]=1;
    for(int i=1;i<=n;++i) que[0][rr[0]++]=read();
    sort(que[0]+1,que[0]+rr[0],cmp);
    
    for(int i=1;i<=m;++i)
    {
    	int x=qmax()+sum;
    	if(i%t==0) printf("%d ",x);
    	
    	int v1=floor((dd)x*p),v2=(dd)x-v1;
    	v1-=sum+q; v2-=sum+q;
    	
    	que[1][rr[1]++]=v1; que[2][rr[2]++]=v2;
    	sum+=q;
    }
    printf("\n");
    for(int i=1;i<=n+m;++i)
    {
        int x=qmax();
        if(i%t==0) printf("%d ",x+sum);
    }
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值