算法竞赛刷题|NOIP2016D2T2-蚯蚓

问题描述 【算法竞赛刷题|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 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<

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; mapcnt; 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<

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 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; queueqq,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 &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; i

    推荐阅读