NOIP 2016 提高组 复赛 第二天 第二题 蚯蚓 earthworm AC代码(单调队列)+15分代码(排序)+35分代码(堆 大顶堆 优先队列)+85分代码(更改堆中元素)
总目录详见:NOIP 提高组 复赛 试题 目录 信奥 历年
在线测评地址:https://www.luogu.com.cn/problem/P2827
读题比较辛苦,参数多,需对照样例来读。
要完整的做完此题,三个样例,都需模拟一遍。
1.AC代码
思路部分摘自:https://blog.csdn.net/chen1352/article/details/53339860
这题打暴力,用堆来做,理论上是可以拿80分,所以比赛还是打暴力好。
但是,正解又短又机智。
我们假设x1>x2。
那么x1肯定会在x2之前就被剪断。
设x1被剪成p1和q1,p1=⌊px1⌋,q1=x1−⌊px1⌋
设x2x2被剪成p2和q2,p2=⌊px2⌋,q2=x2−⌊px2⌋
我们可以得到一个很显然的结论,p1>p2,q1>q2,也就是说p1一定会在p2之前被剪断,q1一定会在q2之前被剪断。
我们来简单的证明一下:
假设x1剪断之后到剪断x2经过了i的时间,
那么此时x2=x2+i∗q,p1=⌊px1⌋+i∗q,q1=x1−⌊px1⌋+i∗q
剪断x2之后p2=⌊p(x2+i∗q)⌋,q2=x2+i∗q−⌊p(x2+i∗q)⌋
我们先比较一下p1和p2p1和p2的大小关系:
我们如果不考虑取整的话,p1−p2=p(x1−x2)+q∗(i−i∗p),因为p是小于1的,所式子一定是大于0的。
p2和q2的大小关系同理。
得证。
那么知道了这个结论之后要怎么去用呢。
我们设三个队列,一个a[0],a[1],a[2]。
a[0]预先存原序列的从大到小的排序。
a[1]存px的从大到小排序
a[2]存x-px的从大到小排序
那么每次取出最大的队首,然后拆掉存到a[1]和a[2]中,根据结论这样一定合法。
那么这道题的思路就很明显了,开三个队列,对于第一个队列,存排好序的初始蚯蚓,第二个队列存被切出来的长度为⌊px⌋的蚯蚓,第三个队列存长度为x-⌊px⌋的蚯蚓,每次找出每条队列中长度最大的蚯蚓进行比较,得到在所有蚯蚓中长度最大的那只,拿出来切开再塞回第二第三条队列就好了。
#include <cstdio>
#include <algorithm>
const int maxlongint=2147483647;
const int mo=1000000007;
const int N=7100005;
using namespace std;
struct ddx{
int v,t;
}d[3][N];
int s[3],t[3],n,m,tt,q,v,u;
bool cmp(ddx x,ddx y){
return x.v>y.v;
}
int get(int i,int x){
if(s[i]>t[i]) return -maxlongint;//队列中无元素
return d[i][s[i]].v+q*(x-d[i][s[i]].t-1);
}
int compete(int x){
int pos=0;
for(int i=0;i<=2;i++){
if(s[i]<=t[i])
if(get(i,x)>get(pos,x)) pos=i;
}
return pos;
}
int main(){
scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&tt);
for(int i=1;i<=n;i++) scanf("%d",&d[0][i].v);
sort(d[0]+1,d[0]+1+n,cmp);
s[0]=s[1]=s[2]=1;//队首位置
t[0]=n;//队尾位置
for(int i=1;i<=m;i++){
int pos=compete(i),val=get(pos,i);//pos在三个队列中的某个队列
if(i%tt==0) printf("%d ",val);
s[pos]++;
d[1][++t[1]].v=int(u*1.0/v*val);
d[2][++t[2]].v=val-int(u*1.0/v*val);
d[1][t[1]].t=i;
d[2][t[2]].t=i;
}
printf("\n");
for(int i=1;i<=n+m;i++){
int pos=compete(m+1),val=get(pos,m+1);
if(i%tt==0) printf("%d ",val);
s[pos]++;
}
}
2.15分代码
思路:
m=0,t=1
因[m/t]=[0/1]=0,注意第一行没有数要输出,但也要输出一个空行。
第二行,只需自大到小输出即可。
15分代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
int a[100010];
int n,m,u,v,q,t;
int cmp(int a,int b){
return a>b;//自大到小输出
}
int main(){
int i;
scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
if(m==0){
sort(a+1,a+1+n,cmp);
printf("\n");
for(i=1;i<=n;i++)printf("%d ",a[i]);
}
return 0;
}
3.35分代码
引入优先队列,按题意操作。
35分代码如下:
#include <cstdio>
#include <algorithm>
#include <queue>
#define LL long long
using namespace std;
LL n,m,q,u,v,t;
LL a[100010],b[7010010];
int cmp(LL a,LL b){
return a>b;
}
priority_queue<LL,vector<LL>,less<LL> > pq[2];//大顶堆
int main(){
int i,now=0,bn;
LL b1,b2,b3;
scanf("%lld%lld%lld%lld%lld%lld",&n,&m,&q,&u,&v,&t);
for(i=1;i<=n;i++)scanf("%lld",&a[i]),pq[now].push(a[i]);
for(i=1;i<=m;i++){
b[i]=pq[now%2].top();
pq[now%2].pop();
b1=b[i]*u/v;
b2=b[i]-b1;
while(!pq[(now+1)%2].empty())pq[(now+1)%2].pop();
pq[(now+1)%2].push(b1);
pq[(now+1)%2].push(b2);
while(!pq[now%2].empty()){
b3=pq[now%2].top(),pq[now%2].pop();
b3+=q;
pq[(now+1)%2].push(b3);
}
now++;
}
for(i=1;i<=m/t;i++)printf("%lld ",b[i*t]);
printf("\n");
for(i=1;i<=n+m;i++)b[i]=0;
bn=0;
while(!pq[now%2].empty()){
b[++bn]=pq[now%2].top();
pq[now%2].pop();
}
sort(b+1,b+1+n+m,cmp);
for(i=1;i<=(n+m)/t;i++)printf("%lld ",b[i*t]);
return 0;
}
4.85分代码
样例1,改进后的优先队列,模拟过程如下:
0s
堆中元素如下,堆顶是3,堆顶将被1s取出
3 3 2
1s
(1,2) 4 3
0s的堆顶被取出,值是3,使用值是3+0=3
堆中元素剩下
3 2
将1-1=0,2-1=1加入堆,
堆中元素如下,堆顶是3,堆顶将被2s取出
0 1 3 2
堆中元素,每个都+1可变成
1 2 4 3
2s
2 3 (1,3) 4
1s的堆顶被取出,值是3,使用值是3+1=4
堆中元素剩下
0 1 2
将1-2=-1,3-2=1加入堆,
堆中元素如下,堆顶是2,堆顶将被3s取出
0 1 2 -1 1
堆中元素,每个都+2可变成
2 3 4 1 3
3s
3 4 2 4 (1,3)
2s的堆顶被取出,值是2,使用值是2+2=4
堆中元素剩下
0 1 -1 1
将1-3=-2,3-3=0加入堆,
堆中元素如下,堆顶是1,堆顶将被4s取出
0 1 -1 1 -2 0
堆中元素,每个都+3可变成
3 4 2 4 1 3
4s
4 (1,3) 3 5 2 4
5s
5 2 4 4 (1,4) 3 5
6s
(1,4) 3 5 5 2 5 4 6
7s
2 5 4 6 6 3 6 5 (2,4)
85分代码如下:
#include <cstdio>
#include <algorithm>
#include <queue>
#define LL long long
using namespace std;
LL n,m,q,u,v,t;
LL a[100010],b[7010010];
int cmp(LL a,LL b){
return a>b;
}
priority_queue<LL,vector<LL>,less<LL> > pq;//大顶堆
int main(){
int i,bn;
LL b1,b2,b3;
scanf("%lld%lld%lld%lld%lld%lld",&n,&m,&q,&u,&v,&t);
for(i=1;i<=n;i++)scanf("%lld",&a[i]),pq.push(a[i]);
for(i=1;i<=m;i++){//算法的时间复杂度O(mlogn)
b[i]=pq.top()+q*(i-1);
pq.pop();
b1=b[i]*u/v,b2=b[i]-b1;
b1-=q*i,b2-=q*i;
pq.push(b1);
pq.push(b2);
}
for(i=1;i<=m/t;i++)printf("%lld ",b[i*t]);
printf("\n");
for(i=1;i<=n+m;i++)b[i]=0;
bn=0;
while(!pq.empty()){
b[++bn]=pq.top()+q*m;
pq.pop();
}
sort(b+1,b+1+n+m,cmp);
for(i=1;i<=(n+m)/t;i++)printf("%lld ",b[i*t]);
return 0;
}