NOIP 2016 提高组 复赛 第二天 第二题 蚯蚓 earthworm AC代码(单调队列)+15分代码(排序)+35分代码(堆 大顶堆 优先队列)+85分代码(更改堆中元素)

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值