BZOJ4721 [Noip2016]蚯蚓

58 篇文章 0 订阅
3 篇文章 0 订阅

Description

本题中,我们将用符号 c 表示对 c 向下取整,例如:3.0=3.1=3.9=3。蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓。蛐蛐国里现在共有 n 只蚯蚓(n为正整数)。每只蚯蚓拥有长度,我们设第 i 只蚯蚓的长度为ai(i=1,2,...,n),并保证所有的长度都是非负整数(即:可能存在长度为 0 的蚯蚓)。每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半。神刀手切开蚯蚓的位置由常数p(是满足 0<p<1 的有理数)决定,设这只蚯蚓长度为 x ,神刀手会将其切成两只长度分别为px xpx 的蚯蚓。特殊地,如果这两个数的其中一个等于 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 个非负整数,为a1,a2,...,an,即初始时 n 只蚯蚓的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。
保证1n105,0<m<7×106,0<u<v<109,0q200,1<t<71,0<ai<108

Output

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

Sample Input

3 7 1 1 3 1
3 3 2

Sample Output

3 4 4 4 5 5 6
6 6 6 5 5 4 4 3 2 2

Data Range

Problem Address

BZOJ4721 UOJ#264 洛谷P2827

Solution

【60-85pts】堆
  • 很容易想到用堆来维护所有蚯蚓的长度:
  • 针对第一问,我们每次取出最长的那只蚯蚓,将其切割后再次插入堆中
  • 针对第二问,我们直接将堆中所有元素按大小顺序取出
  • 但这样还有一个问题:如何处理每次取出后,其余蚯蚓的长度都增加 q
  • 我们另外记一个变量Add,表示当前每条蚯蚓(不考虑被切割的)的长度已经增加了多少,每次取出的时候加上 Add 就表示现在的实际长度(当然,插入堆中时还要再减回去)
  • 考虑到切割后的蚯蚓不能增加长度,我们把问题转化:直接将这切割出的两只蚯蚓长度都减去 q 再插入堆,就解决了这个问题
  • 这样的总复杂度为O((n+m)log(n+m)),应该是能通过大部分数据点的
Code-1
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>

using namespace std;
const int N = 1e5 + 5, M = 7e6 + 5;
int n, m, q, u, v, t, Add;
double p;

struct BigRt
{
    int g[N + M], l;

    inline void Pop()
    {
        g[1] = g[l--];
        int now = 1, nxt = 2, res = g[1];
        while (nxt <= l)
        {
            if (nxt < l && g[nxt | 1] > g[nxt]) nxt |= 1;
            if (res < g[nxt])
             g[now] = g[nxt], nxt = (now = nxt) << 1;
            else break;
        }
        g[now] = res;
    }

    inline void Push(const int &res)
    {
        g[++l] = res;
        int now = l, nxt = l >> 1;
        while (nxt)
        {
            if (res > g[nxt])
             g[now] = g[nxt], nxt = (now = nxt) >> 1;
            else break;
        }
        g[now] = res;
    }
}Q;

inline int get()
{
    char ch; int res = 0; bool f = true;
    while (((ch = getchar()) < '0' || ch > '9') && ch != '-');
    if (ch == '-') f = false; 
     else res = ch - '0';
    while ((ch = getchar()) >= '0' && ch <= '9')
     res = (res << 3) + (res << 1) + ch - '0';
    return f? res : -res;
}

inline void put(int x)
{
    if (x < 0)
     x = -x, putchar('-'); 
    if (x > 9) put(x / 10);
    putchar(x % 10 + 48); 
}

inline bool cmp(const int &x, const int &y) {return x > y;}

int main()
{
    n = get(); m = get(); q = get(); 
    u = get(); v = get(); t = get();
    p = (double)u / v; Q.l = 0;
    for (int i = 1; i <= n; ++i) Q.Push(get());
    for (int i = 1; i <= m; ++i)
    {
        int x = Q.g[1] + Add; Q.Pop(); 
         if (i % t == 0) put(x), putchar(' '); 
        int l = (int)(p * x), r = x - l; 
        Q.Push(l - Add - q); Q.Push(r - Add - q);
        Add += q; 
    }
    putchar('\n');
    for (int i = 1; i <= n + m; ++i)
    { 
        if (i % t == 0) put(Q.g[1] + Add), putchar(' ');
        Q.Pop();    
    }
}
【100pts】队列
  • 我们会发现蚯蚓的切割具有单调性:一只蚯蚓切割后会分为 px xpx 两个部分,对于其中的任意一个部分,在某一时刻切割出的那只蚯蚓必然会比在它之后切割出来的蚯蚓要长
  • 我们用反证法对此予以证明:
  • 设某一时刻被选出的某只蚯蚓切割前的长度为 ai ,经过 N 秒后,假设存在一只之前未被切割过的蚯蚓这一秒切割完后长度最大,我们记其N秒前的长度为 aj ,那么 ai,aj 必然要满足(我们先只考虑切割出的 px 那部分蚯蚓, xpx 同理):
    ai×p+N×q(aj+N×q)×p
    ai×p+N×qaj×p+N×q×p
  • 又因为 N 秒前长度为ai的蚯蚓被选出,所以那一时刻满足 aiaj ,而 p 的取值范围为0<p<1,所以必然满足
    ai×p+N×q>aj×p+N×q×p
  • 与之前的假设矛盾,因此上述情况不可能存在,我们证得蚯蚓的切割具有单调性
  • 考虑记录三个队列,分别存储未切割过的蚯蚓和切割成的两只蚯蚓,每次将蚯蚓插入对应的队尾。根据我们上面推论得出的单调性,每次取出三个队头的最大值即可,蚯蚓长度的增加和上述堆做法的处理方式相同,这样的总复杂为 O(n+m)
Code-2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>

using namespace std;
typedef long long ll;
const int Maxn = 2147483647;
const int N = 1e5 + 5, M = 7e6 + 5;
int n, m, q, u, v, t, Add;
int Q[3][M], qt[3], qw[3]; 

inline int get()
{
    char ch; int res;
    while ((ch = getchar()) < '0' || ch > '9');
    res = ch - '0';
    while ((ch = getchar()) >= '0' && ch <= '9')
     res = (res << 3) + (res << 1) + ch - '0';
    return res;
}

inline void put(int x)
{ 
    if (x > 9) put(x / 10);
    putchar(x % 10 + 48); 
}

inline bool cmp(const int &x, const int &y) {return x > y;}

inline int GetMax()
{
    int res = -Maxn, k;
    for (int i = 0; i < 3; ++i)
     if (qt[i] < qw[i] && res < Q[i][qt[i] + 1])
      res = Q[i][qt[i] + 1], k = i;
    qt[k]++; return res;
}

int main()
{
    n = get(); m = get(); q = get(); 
    u = get(); v = get(); t = get();
    for (int i = 1; i <= n; ++i) Q[0][++qw[0]] = get(); 
    sort(Q[0] + 1, Q[0] + qw[0] + 1, cmp);
    for (int i = 1; i <= m; ++i)
    {
        int x = GetMax() + Add;
        if (i % t == 0) put(x), putchar(i + t > m ? '\n' : ' '); 
        int l = (ll)x * u / v, r = x - l; 
        Q[1][++qw[1]] = l - Add - q;
        Q[2][++qw[2]] = r - Add - q; Add += q; 
    }
    if (t > m) putchar('\n');
    int tmp = n + m;
    for (int i = 1; i <= tmp; ++i)
    {
        int x = GetMax() + Add;
        if (i % t == 0) {put(x); if (i + t <= tmp) putchar(' ');}
    }
    return 0;
}

Notice

  • BZOJ 上提交注意输出格式:行末无空格,第二行无回车
  • UOJ 上提交注意精度问题:计算切断后蚯蚓长度直接 ×u/v ,过程中可能会爆 int ,应强转 long_long
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值