【NOIP2016提高组】蚯蚓

Description
这里写图片描述
Input
这里写图片描述
Output
这里写图片描述

Sample Input

Sample Input1:
3 7 1 1 3 1
3 3 2

Sample Output1:
3 4 4 4 5 5 6
6 6 6 5 5 4 4 3 2 2

The Solution

搬来的。。
题目大意
每一轮有若干个正整数,每一轮会选出最大的一个(设其为 x)并将 x 用两个数取代之,一个是 xp ,一个是 xxp
其中 p 为一个取值范围为(0,1)的实数,而其余的数都会加上一个非负整数 q。一开始有 n 个数,要进行 m 轮操作,问每轮操
作删掉的数和最后剩下的数。
f1 ≤ n ≤ 10
5
, 1 ≤ m ≤ 7 × 10
6
解题思路
先来考虑这样一个东西:
假设当前轮的数中最大为 x,y 为其余数中任意一个。
显然这一轮之后 x 会变成 xp xxp ,设 y 会在 k 轮之后被选出来,那么到时候 y 会变成 y×p+k×q×p yy×p+k×q×p
那么在之后的第 k 轮里,x 和 y 会变成⌊x × p⌋ + k × q,x + k × q − ⌊x × p⌋,⌊y × p + k × q × p⌋和 y − ⌊y × p + k × q × p⌋
考虑⌊x × p⌋ + k × q和⌊y × p + k × q × p⌋的大小关系:
将k × q放进下取整的式子里得到:⌊x × p + k × q⌋
又因为x ≥ y,p ∈ (0,1)
所以⌊x × p + k × q⌋ > ⌊y × p + k × q × p⌋
类似的,可以得到:x + k × q − ⌊x × p⌋ > y − ⌊y × p + k × q × p⌋
那么这个意味着什么?
我们将每一轮的所有数分成三类:
1、最开始的数
2、是由某一轮的最大值 x 得到的⌊x × p⌋
3、是由某一轮的最大值 x 得到的x − ⌊x × p⌋
那么开始的证明意味着第 2、3 种数中每次加入的数是不上升的。
于是我们得到这样一个算法:
对于每一种数我们维护一个单调的队列(三种就是说有 3 个队列),一开始只有第 1 种数有 n 个数,从大到小排序放进队列
里。
每一轮在三个队头中取出最大的(由于队列单调不上升,这个数必然是当前所有数中最大的),设其为 x,然后将 x 分成两个
数⌊x × p⌋,一个是x − ⌊x × p⌋,设 a=⌊x × p⌋,b= x − ⌊x × p⌋,将 a 放到第二个队列队尾,b 放到第三个队列队尾并将 x 从其
自己的队列中弹出。
对于每一轮其他数都加上 q 的操作可以通过记录某个数第一次出现的时间,即假设当前为第 i 轮,x 为这个数最开始的值,t
为出现时间,那么当前这个数为 x+(t-i)*q。
于是完美解决,空间复杂度 O(m)时间复杂度 O(m)

CODE

手打的 (被卡常了。。。改改数组大小。。。)

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define N 7000005
#define INF 2147483647

using namespace std;

typedef double db;

int n,m,q,u,v,t;
int b[3],c[3],g[3][N], d[3][N];

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

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

int Calc(int i,int x)
{
    if (b[i] > c[i]) return - INF;
    return d[i][b[i]] + q * (x - g[i][b[i]] - 1);
}

int Pos(int x)
{
    int wz = 0;
    fo(i,0,2)
        if (b[i] <= c[i]) if (Calc(i,x) > Calc(wz,x)) wz = i;   
    return wz;
}

int main()
{
    freopen("earthworm.in","r",stdin);
    freopen("earthworm.out","w",stdout);
    n = read(),m = read(),q = read(),u = read(),v = read(),t = read();  
    fo(i,1,n) d[0][i] = read();
    sort(d[0]+1,d[0]+1+n,cmp);
    b[0] = b[1] = b[2] = 1;
    c[0] = n;
    fo(i,1,m)
    {
        int wz = Pos(i), len = Calc(wz,i);
        if (i % t == 0) printf("%d ",len);
        b[wz] ++;
        d[1][++ c[1]] = int((u*1.0 / v * len));
        d[2][++ c[2]] = len - int( u*1.0 / v * len);
        g[1][c[1]] = i;
        g[2][c[2]] = i;
    }
    printf("\n");
    fo(i,1,n+m)
    {
        int wz = Pos(m + 1) , len = Calc(wz,m + 1);
        if (i % t == 0) printf("%d ",len);
        b[wz] ++;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值