[NOIP2016][单调队列]蚯蚓

题目描述:
题目链接: UOJ 264 http://uoj.ac/problem/264
题目背景: NOIP2016 提高组 Day2 T2
本题中,我们将用符号这里写图片描述表示对 c 向下取整,例如:这里写图片描述
蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓。
蛐蛐国里现在共有 n 只蚯蚓(n为正整数)。每只蚯蚓拥有长度,我们设第 i 只蚯蚓的长度为 ai(i=12...n) ,并保证所有的长度都是非负整数(即:可能存在长度为0的蚯蚓)。
每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半。神刀手切开蚯蚓的位置由常数 p(是满足 0<p<1 的有理数)决定,设这只蚯蚓长度为 x ,神刀手会将其切成两只长度分别为 这里写图片描述 的蚯蚓。特殊地,如果这两个数的其中一个等于 0 ,则这个长度为 0 的蚯蚓也会被保留。此外,除了刚刚产生的两只新蚯蚓,其余蚯蚓的长度都会增加 q(是一个非负整常数)。
蛐蛐国王知道这样不是长久之计,因为蚯蚓不仅会越来越多,还会越来越长。蛐蛐国王决定求助于一位有着洪荒之力的神秘人物,但是救兵还需要 m 秒才能到来……(m为非负整数)
蛐蛐国王希望知道这 m 秒内的战况。具体来说,他希望知道:
m 秒内,每一秒被切断的蚯蚓被切断前的长度(有 m 个数);
m 秒后,所有蚯蚓的长度(有 n+m 个数)。
蛐蛐国王当然知道怎么做啦!但是他想考考你……
输入格式:
第一行包含六个整数 n,m,q,u,v,t,其中:n,m,q 的意义见【问题描述】;u,v,t 均为正整数;你需要自己计算 p=u/v (保证 0<u<v );t 是输出参数,其含义将会在【输出格式】中解释。
第二行包含 n 个非负整数,为 a1,a2,… ,an,即初始时 n 只蚯蚓的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。
保证 1n1050m7×1060uv1090q2001t710ai108
输出格式:
第一行输出这里写图片描述个整数,按时间顺序,依次输出第 t 秒,第 2t 秒,第 3t 秒,……被切断蚯蚓(在被切断前)的长度。
第二行输出这里写图片描述个整数,输出 m 秒后蚯蚓的长度;需要按从大到小的顺序,依次输出排名第 t,第 2t,第 3t,……的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。即使某一行没有任何数需要输出,你也应输出一个空行。
请阅读样例来更好地理解这个格式。
样例输入1:
3 7 1 1 3 1
3 3 2
样例输出1:
3 4 4 4 5 5 6
6 6 6 5 5 4 4 3 2 2
样例输入2:
3 7 1 1 3 2
3 3 2
样例输出2:
4 4 5
6 5 4 3 2
样例输入3:
3 7 1 1 3 9
3 3 2
样例输出3:

2
备注:
【样例1说明】
在神刀手到来前:3 只蚯蚓的长度为 3,3,2。
【样例2说明】
这个数据中只有 t=2 与上个数据不同。只需在每行都改为每两个数输出一个数即可。
虽然第一行最后有一个 6 没有被输出,但是第二行仍然要重新从第二个数再开始输出。
【样例3说明】
这个数据中只有 t=9 与上个数据不同。
注意第一行没有数要输出,但也要输出一个空行。

1 秒后:一只长度为 3 的蚯蚓被切成了两只长度分别为 1 和 2 的蚯蚓,其余蚯蚓的长度增加了 1 。最终 4 只蚯蚓的长度分别为 (1,2),4,3 。括号表示这个位置刚刚有一只蚯蚓被切断。
2 秒后:一只长度为 4 的蚯蚓被切成了 1 和 3 。5 只蚯蚓的长度分别为:2,3,(1,3),4 。
3 秒后:一只长度为 4 的蚯蚓被切断。6 只蚯蚓的长度分别为:3,4,2,4,(1,3)。
4 秒后:一只长度为 4 的蚯蚓被切断。7 只蚯蚓的长度分别为:4,(1,3),3,5,2,4。
5 秒后:一只长度为 5 的蚯蚓被切断。8 只蚯蚓的长度分别为:5,2,4,4,(1,4),3,5。
6秒后:一只长度为5的蚯蚓被切断。9只蚯蚓的长度分别为:(1,4),3,5,5,2,5,4,6。
7 秒后:一只长度为 6 的蚯蚓被切断。10只蚯蚓的长度分别为:2,5,4,6,6,3,6,5,(2,4)。
所以,7 秒内被切断的蚯蚓的长度依次为 3,4,4,4,5,5,6。7 秒后,所有蚯蚓长度从大到小排序为 6,6,6,5,5,4,4,3,2,2。
数据规模与约定
测试点 1~3 满足 m=0。
测试点 4~7 满足 n,m≤1,000。
测试点 8~14 满足 q=0,其中测试点 8~9 还满足 m≤105。
测试点 15~18 满足 m≤3×105。
测试点 19~20 没有特殊的约定,参见原始的数据范围。
测试点 1~12,15~16 还满足 v≤2 ,这意味着 u,v 的唯一可能的取值是 u=1,v=2,即 p=0.5。这可能会对解决问题有特殊的帮助。
每个测试点的详细数据范围见下表。
这里写图片描述
题目分析:
根据题目数据可知要有一个 O(m) 的算法。用堆(队列优化)可得75分,因为多了一个 log(n+m)
考虑这样一个性质:第一只被斩的蚯蚓分成a1,b1两段,后来再被斩的一只分成a2,b2两段。可知在同一秒内 a1>a2,b1>b2 ,原因在于第一只本身必定比第二只长,而它们的增加量其实是一样的,所以斩后得到的a,b单调递减。于是可以再开两个数组,记录a,b;每次从原队列(排过序)和a、b队列中选取最大的,拿出来斩,斩后又加入a、b队列(根据前面的分析a、b直接加入也是有序的)。最后根据题目要求输出。
注:NOIP2016正式的数据,直接用 double p=u/v 就可过,但是uoj里面那些hack的数据卡精度 long double 都不行。只能long long 定义u,v然后每次算的时候直接 u/v ,不预处理p。
附代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<queue>
#include<iomanip>
#include<cmath>
#include<map>
#include<set>
#include<cctype>
#include<algorithm>
using namespace std;

const int maxn=1e7+10;
int n,m,t,q,q1[maxn],q2[maxn],q3[maxn]; 
int head1,head2,head3,tail1,tail2,tail3;
long long u,v,x,y,z;


inline int readint()
{
    char ch;int i=0,f=1;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        ch=getchar();
        f=-1;
    }
    for(;ch>='0'&&ch<='9';ch=getchar())
        i=(i<<3)+(i<<1)+ch-'0';
    return i*f;
}

inline void write(int x)//输出优化
{
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) write(x / 10);
    putchar(x%10+'0');
}

inline int find(int num)
{
    int a=-1,b=-1,c=-1,d=-1;
    if(head1<=tail1) b=q1[head1]+num*q;
    if(head2<=tail2) c=q2[head2]+num*q;
    if(head3<=tail3) d=q3[head3]+num*q;
    a=max(b,max(c,d));
    if(a==b) head1++;
    else
    {
        if(a==c) head2++;
        else head3++;
    }   
    return a;
} 

bool comp(const int &a,const int &b)
{
    return a>b;
}

int main()
{
    //freopen("lx.in","r",stdin);
    //freopen("lx.out","w",stdout);

    n=readint();m=readint();q=readint();u=readint();v=readint();t=readint();
    for(int i=1;i<=n;i++)
    {
        x=readint();
        q1[i]=x;
    }
    sort(q1+1,q1+n+1,comp);
    tail1=n;head1=head2=head3=1;
    for(int i=1;i<=m;i++)
    {   
        x=find(i-1);
        if(i%t==0) write(x),putchar(' ');
        y=x*u/v;
        z=x-y;
        y=y-i*q;
        z=z-i*q;
        q2[++tail2]=y;
        q3[++tail3]=z;
    }
    putchar('\n');
    for(int i=1;i<=n+m;i++)
    {
        x=find(m);
        if(i%t==0) write(x),putchar(' ');
    }

    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值