【bzoj4721】【noip2016】蚯蚓
题目描述
本题中,我们将用符号⌊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个数)。
蛐蛐国王当然知道怎么做啦!但是他想考考你......
输入
第一行包含六个整数n,m,q,u,v,t,其中:n,m,q的意义见【问题描述】;u,v,t均为正整数;你需要自己计算p = u/v (保证0<u<v) t是输出参数,其含义将会在 【输出格式】中解释。
第二行包含n个非负整数,为a1, a2, ..., an,即初始时n只蚯蚓的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。
保证 1<=n<=10^5,0 <= m <= 7 x 10^6, 0 < u < v <= 10^9,0 <= q <= 200, 1 <= t <= 71, 0 <= ai <= 10^8。
输出
第一行输出个整数,按时间顺序,依次输出第t秒,第2t秒,第3t秒,……被切断蚯蚓(在被切断前)的长度。
第二行输出个整数,输出m秒后蚯蚓的长度;需要按从大到小的顺序,依次输出排名第t,第2t,第3t,…… 的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。即使某一行没有任何数需要输出,你也应输出一个空行。
请阅读样例来更好地理解这个格式。
样例输入1
3 7 1 1 3 1
3 3 2
样例输出1
3 4 4 4 5 5 6
6 6 6 5 5 4 4 3 2 2
样例输入2
3 7 1 1 3 2
3 3 2
样例输出2
4 4 5
6 5 4 3 2
样例输入3
3 7 1 1 3 9
3 3 2
样例输出3
2
题解
80分做法:建立一个大根堆,堆顶元素就是最大的元素,切成两段后再放入堆中。但是每秒蚯蚓的长度都会增加,是否需要修改堆里元素的值?其实并不需要,每次取出最大元素后,将最大值还原,例如现在是第t秒,最大值是max,那么这个最大值还原后就应该为 max + ( t-1 ) * q,将这个长度分成两端,再减去 t * q ,再加入堆中。堆可以用STL,代码很好实现。
80分代码:
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=1e5+5; const int maxm=7e6+50; ll heap[maxm+maxn],x1,x2,x,ans1[maxm],ans2[maxn+maxm],a[maxn]; int m,q,u,v,t,len,n,o; void put(int d){ int now,next; heap[++len]=d; now=len; while (now>1){ next=now>>1; if (heap[now]<=heap[next])return; swap(heap[now],heap[next]); now=next; } } int get(){ int now,next,res; res=heap[1]; heap[1]=heap[len--]; now=1; while (now*2<=len){ next=now*2; if (next<len && heap[next+1]>heap[next]) next++; if (heap[now]>=heap[next]) return res; swap(heap[now],heap[next]); now=next; } return res; } template<typename T>void read(T& aa){ char cc; ll ff;aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int main(){ read(n),read(m),read(q),read(u),read(v),read(t); for(int i=1;i<=n;i++) read(a[i]),put(a[i]); for(int i=1;i<=m;i++){ x=get();x=x+(i-1)*q;ans1[i]=x; x1=int(x*u/v); x2=x-x1; x1-=q*i; x2-=q*i; put(x1),put(x2); } if(t>m) cout<<endl; else { for(int i=1;i<=m/t;i++){ o+=t; cout<<ans1[o]<<" "; } cout<<endl; } for(int i=1;i<=n+m;i++) ans2[i]=get(); o=0; for(int i=1;i<=(m+n)/t;i++){ o+=t; cout<<ans2[o]+m*q<<" "; } return 0; }
100分:80分的做法需要维护最大值,多了一个log,于是被卡。如何不用维护最大值?
开3个队列,q1,q2,q3。q1存原 a[ i ] ,q2存每次切了后长度为 p*x 的新蚯蚓,q3存每次切了后长度为 1-p*x 的新蚯蚓。
因为每次在q1里选最大的切,那么先放入q2,q3的蚯蚓一定比后放入的长度大,即q2,q3是单调的,不需要维护。
那么只要在最开始对 a[ i ] 排序,q1就也是单调的了。
每秒钟,在三个队列里选择最大值,切掉,切后放入q2,q3,复杂度为O(m)。
然后从大到小输出这三个队列里的元素,复杂度O(n+m)。
总复杂度:O(n+m)。
100分代码:
#include<queue> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=1e5+50; const int maxm=7e6+50; ll n,m,q,u,v,t,a[maxn],len[maxm]; queue<ll>q1,q2,q3; int cmp(ll x,ll y){return x>y;} template<typename T>void read(T& aa){ char cc; ll ff;aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int main(){ read(n),read(m),read(q),read(u),read(v),read(t); for(int i=1;i<=n;i++) read(a[i]); sort(a+1,a+1+n,cmp); for(int i=1;i<=n;i++) q1.push(a[i]); for(int i=1;i<=m;i++){ ll a1=q1.front()+(i-1)*q,a2=q2.front()+(i-1)*q,a3=q3.front()+(i-1)*q,s1,s2; if(q1.empty()) a1=0; if(q2.empty()) a2=0; if(q3.empty()) a3=0; if(a1>=a2&&a1>=a3&&!q1.empty()){ len[i]=a1; q1.pop();s1=a1*u/v,s2=a1-s1; s1-=i*q;s2-=i*q; q2.push(s1),q3.push(s2); } else if(a2>=a1&&a2>=a3){ len[i]=a2; q2.pop();s1=a2*u/v,s2=a2-s1; s1-=i*q;s2-=i*q; q2.push(s1),q3.push(s2); } else if(a3>=a1&&a3>=a2){ len[i]=a3; q3.pop();s1=a3*u/v,s2=a3-s1; s1-=i*q;s2-=i*q; q2.push(s1),q3.push(s2); } } for(int i=1;i<=m;i++) if(i%t==0) printf("%d ",len[i]); printf("\n");int tt=0; while(!q1.empty()||!q2.empty()||!q3.empty()){ tt++; ll a1=q1.front()+m*q,a2=q2.front()+m*q,a3=q3.front()+m*q; if(q1.empty()) a1=0; if(q2.empty()) a2=0; if(q3.empty()) a3=0; if(a1>=a2&&a1>=a3&&!q1.empty()){ q1.pop();if(tt%t==0) printf("%d ",a1); } else if(a2>=a1&&a2>=a3&&!q2.empty()){ q2.pop();if(tt%t==0) printf("%d ",a2); } else if(a3>=a1&&a3>=a2&&!q3.empty()){ q3.pop();if(tt%t==0) printf("%d ",a3); } } return 0; }