NOIP2016D2T2-蚯蚓

问题描述

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

3 7 1 1 3 1
3 3 2

样例输出

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

思路分析&代码:

1.先就想到了set,于是就用了set于是就写了一个智障代码,CE,因为set只能用迭代器..

智障代码

#include 
#include 
#include 
using namespace std;
int main(){
    set<int> s;
    int n,m,q,u,v,t;
    cin>>n>>m>>q>>u>>v>>t;
    for(int i=1;i<=n;i++){
        int a;
        cin>>a;
        s.insert(a);
    }
    double p=u/v;
    for(int i=1;i<=m;i++){
        int t2=s[n+m-2];
        if(i%t==0)cout<2]<<" ";
        s.erase(s[m+n-2]);
        for(int i=m+n-3;i>=0;i--)s[i]+=q;
        s.insert(int(p*t2));
        s.insert(t2-int(p*t2));
    }
    cout<for(int i=1;i<=n;i++){
        if(i%t==0)cout<return 0;
}

set是从小到大排序且set的迭代器不可以执行it+变量,只能用it++运算符,所以我就写了一个结构体,重载<运算符,如下

struct QY{
    int l;
    bool operator < (const QY& b)const{
        if(this->l>b.l)return true;
        return false;
    }
};

总算是有一个从大到小的序列了,但是set有一个问题就是,集合是不可重复的!所以又写了一个map记录一个长度出现了多少次如果次数为0就在set里删除元素,于是就有了:

void myinsert(QY a){
    if(!cnt.count(a.l)){cnt[a.l]=0;s.insert(a);}
    cnt[a.l]++;
    return;
}

void myerase(QY a){
    cnt[a.l]--;
    if(cnt[a.l]==0){cnt.erase(a.l);s.erase(a);}
    return;
}

然后就是一波模拟,但是map慢。。。TLE了13组,所以说这是不可行的

WA+TLE代码(set+map)

#include 
#include 
#include 
#include 
using namespace std;

map<int,int>cnt;
struct QY{
    int l;
    bool operator < (const QY& b)const{
        if(this->l>b.l)return true;
        return false;
    }
};
set s;

void myinsert(QY a){
    if(!cnt.count(a.l)){cnt[a.l]=0;s.insert(a);}
    cnt[a.l]++;
    return;
}

void myerase(QY a){
    cnt[a.l]--;
    if(cnt[a.l]==0){cnt.erase(a.l);s.erase(a);}
    return;
}

int main(){
    int n,m,q,u,v,t;
    cin>>n>>m>>q>>u>>v>>t;
    double p=u/v;

    for(int i=1;i<=n;i++){
        QY tmp;
        scanf("%d",&tmp.l );
        myinsert(tmp);
    }

    set::iterator it;


    for(int i=1;i<=m;i++){
    it=s.begin();
    QY tmp=*it;
    if(i%t==0)printf("%d ",tmp.l);
    myerase(tmp);
    for(it=s.begin();it!=s.end();it++){
        QY tmp2=*it;
        tmp2.l +=q;
    }
    tmp.l=t*p;
    myinsert(tmp);
    tmp.l=t-tmp.l;
    myinsert(tmp);
    }

    cout<for(int i=1;i<=n+m;it++){
        QY tmp=*it; 
        for(int j=0;jif(i%t==0){printf("%d ",tmp.l);}
        }
    }
    return 0;
}

map慢让我们不得不去找一种其他方法
1.pair<数据,出现次数>
2.结构体里加个出现出现次数
但两个办法有个问题就是set查找会出现查不了,因为查的时候不知道出现次数s.count()括号里数据类型是结构体或pair而参数数不全是不可以的
所以set不行
再想有什么类似于set但数据可重复?
优先队列!
但是有一个问题是优先队列没有指针!不可能用迭代器的方式修改数据(增加蚯蚓长度)当然,可以加一个时间戳(入队时间),就像lazy标记一样在使用时加上((当前时间-时间戳)*q),但是时间戳怎么存呢?这回连map都不行了,长度一样时间戳可能不同!,所以不可行,还有办法,例如
在时间为i的时候要入队,那么就给这个要入队的数据减去i*q那么在出队的时候我就认为他是在开始时入队的,加上出队时间*q正好抵消掉减的那一部分了,而且后来者数据就是小不需要考虑因为延时带来的出队出错!至于优先队列的常数大,会被卡,最多80分(开了O2优化也是),这是我们可以用下面的三队列的方法

优先队列代码

#include 
#include 
#include 
#include 
using namespace std;
priority_queue<int> que;
int m,n,q,u,v,t,add=0;
int main(){
cin>>n>>m>>q>>u>>v>>t;
for(int i=1;i<=n;i++){
    int tmp;
    scanf("%d",&tmp);
    que.push(tmp);
}
for(int i=1;i<=m;i++){
    int tmp=que.top();
    que.pop();
    tmp+=add;
    int a=(long long)tmp*u/v;
    int b=tmp-a;
    que.push(a-add-q);
    que.push(b-add-q);
    if(i%t==0)printf("%d ",tmp);
    add+=q;
}
printf("\n");
for(int i=1;i<=n+m;i++){
    if(i%t==0)printf("%d ",que.top()+add);
    que.pop();
}
return 0;
}

还有办法:
假设有两只蚯蚓长度分别为x,y,且x>y;那么根据题意x一定被先切,于是我们假设y在x被切后i秒被切。
假设x被切成x1和x2两段,y被切成y1和y2两段;
假设x1>x2 y1>y2随便写的,这个关系怎么写都可以
那么x1>y1,x2>y2
于是我们开3个队列
qs存储未被切的,(已从大到小排序)
q1存储 p*x,
q2存储 x-p*x;
由前面可得每个队列都是单调的(此处即由大到小的)
于是每次取3个堆首的最大元素模拟即可
关于时间戳同上

三队列代码

#include 
#include 
#include 
#include 
#include 

using namespace std;

queue<int>qq,q1,q2;
int n,m,q,u,v,t,tmp[100050],add=0;

int cmp(int a,int b){return a>b;}

int getfront(queue<int> &q){
    if(q.empty())return -0x3f3f3f3f;
    return q.front()+add;
}

int compare_each_front(){
    int a=getfront(qq),b=getfront(q1),c=getfront(q2);
    int maxn=max(a,max(b,c));
    if(maxn==a) qq.pop();
    else if(maxn==b) q1.pop();
    else if(maxn==c) q2.pop();
    return maxn;
}

int main(){
    cin>>n>>m>>q>>u>>v>>t;
    for(int i=0;iscanf("%d",&tmp[i]);
    sort(tmp,tmp+n,cmp);
    for(int i=0;ifor(int i=1;i<=m;i++,add+=q){
        int tmp=compare_each_front();
        int a=(long long)tmp*u/v;
        int b=tmp-a;
        q1.push((int)a-add-q);
        q2.push((int)b-add-q);
        if(i%t==0)cout<" ";
    }
    cout<for(int i=1;i<=n+m;i++){
        int tmp=compare_each_front();   
        if(i%t==0)printf("%d ",tmp);
    }
    return 0;

}

你可能感兴趣的:(算法竞赛刷题,#,NOIP提高组复赛,NOIP提高组真题题解)