UOJ264 NOIP2016 day2 T2 蚯蚓(队列)

53 篇文章 0 订阅
1 篇文章 0 订阅

UOJ264 NOIP2016 day2 T2 蚯蚓

原题地址http://uoj.ac/problem/264

题意:
本题中,我们将用符号 ⌊c⌋ 表示对 c 向下取整,例如:⌊3.0⌋=⌊3.1⌋=⌊3.9⌋=3。

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

每一秒,神刀手会在所有的蚯蚓中找到最长的那一只(如有多个则任选一个)将其切成两半。神刀手切开蚯蚓的位置由常数 p(是满足 0< p<1 的有理数)决定,设这只蚯蚓长度为 x,神刀手会将其切成两只长度分别为 ⌊px⌋ 和 x−⌊px⌋ 的蚯蚓。特殊地,如果这两个数的其中一个等于 0,则这个长度为 0 的蚯蚓也会被保留。此外,除了刚刚产生的两只新蚯蚓,其余蚯蚓的长度都会增加 q(是一个非负整常数)。

蛐蛐国王决定求助于一位有着洪荒之力的神秘人物,但是救兵还需要 m 秒才能到来……(m 为非负整数)。

蛐蛐国王希望知道这 m 秒内的战况。具体来说,他希望知道:

m 秒内,每一秒被切断的蚯蚓被切断前的长度(有 m 个数);
m 秒后,所有蚯蚓的长度(有 n+m 个数)。
数据范围
保证 1≤n≤1e5,0≤m≤7×1e6
0< u < v≤1e9,0≤q≤200,1≤t≤71,0≤ai≤1e8。
题解:
虽说暴力又好想又好写,但是正解真的很妙。
假设有x,y两只蚯蚓,满足len x>=len y,x被切的两部分分别为a1,b1,y被切的两部分分别为a2,b2。那么有一个性质 a1>=a2 ,b1>=b2。
想一下,a1+b1=(原)x,a2+b1=(原)y。
时间对他们的作用是相同的,又因为均是成比例切开,因此仍然满足原来的大小关系。
因此,我们可以不必维护堆,因为其本身就有单调性。
维护三个队列 Q0,Q1,Q2。Q1存放原来的大小顺序,Q2存放切出的所有a部分,Q2存放切出的所有b部分。每次只需取三个队队首最大的那个,再把切出来的两个部分放入对应队队尾即可。
注意维护时间戳(保存距离上一次被切过了多少秒)来算现在的长度。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int N=100010;
const int M=7000010;
int n,m,q,u,v,t;
struct node
{
    int len,cnt;
    node(int len,int cnt): len(len),cnt(cnt){}
};
queue<node> Q[3];
int ll[N];
void solve()
{
    int kk=0;
    for(int inc=1;inc<=m;inc++)
    {
        int opt=-1; int mx=-1;
        for(int i=0;i<3;i++)
        {
            if(!Q[i].empty()&&(Q[i].front().len+(inc-1-Q[i].front().cnt)*q>mx))
            {mx=Q[i].front().len+(inc-1-Q[i].front().cnt)*q; opt=i;}     
        }
        node tmp= Q[opt].front(); Q[opt].pop();
        int len=tmp.len+(inc-1-tmp.cnt)*q;
        kk++;
        if(kk%t==0)
        {
            printf("%d",len);
            if(kk!=(m/t)*t)  printf(" ");
        }
        int s1=(1LL*len*u)/v; int s2=len-s1;
        Q[1].push(node(s1,inc)); Q[2].push(node(s2,inc));
    }
    printf("\n");
}
void print()
{
    int kk=0;
    while(1)
    {

        if(Q[0].empty()&&Q[1].empty()&&Q[2].empty()) break; 
        int opt=-1; int mx=-1;
        for(int i=0;i<3;i++)
        {
            if(!Q[i].empty()&&(Q[i].front().len+(m-Q[i].front().cnt)*q>mx))
            {mx=Q[i].front().len+(m-Q[i].front().cnt)*q; opt=i;}    
        }
        node tmp= Q[opt].front(); Q[opt].pop();
        int len=tmp.len+(m-tmp.cnt)*q; 
        kk++;
        if(kk%t==0)
        {
            printf("%d",len);
            if(kk!=((m+n)/t)*t)  printf(" ");
        }
    }
    printf("\n");
}
int main()
{
    scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
    for(int i=1;i<=n;i++)
    scanf("%d",&ll[i]); 
    sort(ll+1,ll+n+1);
    for(int i=n;i>=1;i--)
    Q[0].push(node(ll[i],0));
    solve();
    print();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值