蚯蚓

洛谷 P2827 蚯蚓

首先最容易想到的
85分 优先队列

#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
priority_queue<int> qy;
int main(){
	int n,m,t,q;
	double u,v,p;
	scanf("%d %d %d %lf %lf %d",&n,&m,&q,&u,&v,&t);
	p=u/v;
	for(int i=0;i<n;++i)
	{
		int d;
		scanf("%d",&d);
		qy.push(d);
	}
	int ans=0;
	for(int i=1;i<=m;++i)
	{
		int d=qy.top()+ans;//要先还原,不然计算d1 d2会出错 
		qy.pop();
		if(!(i%t))
			printf("%d ",d);
		int d1=floor(p*double(d)),d2=d-d1;
		d1-=ans;
		d2-=ans;
		d1-=q;
		d2-=q;
		//减掉加上的ans,由于d1 d2少加一次q,先减掉一次 
		//printf("%d %d %d\n",d,d1,d2);
		qy.push(d1);
		qy.push(d2);
		ans+=q;
	}
	printf("\n");
	for(int i=1;i<=n+m;++i)
	{
		int d=qy.top();
		if(!(i%t))
			printf("%d ",d+ans);
		qy.pop();
	}
	printf("\n");
	return 0;
}

看完题解…emmm
解释摘了aiyougege的题解

关键点:
发现此题中隐含的单调性.
发现先被切掉的蚯蚓分成的蚯蚓一定比后切掉的蚯蚓分成的蚯蚓大.

证明:
假设这两只蚯蚓分别为a,b,其中a>b.
那么它被切成a1,a2,t秒后, b被切成了b1,b2
此时a1,a2的长度为p*a+q*t,(1-p)*a+q*t
而b1,b2的长度却为p*(b+q*t), (1-p)*(b+q*t)
易看出a1>b1,a2>b2 .
也就是说根本不需要用一个堆来维护, 它本来就具有一定单调性.

那么就可以将这两堆依次存储, 加上还没被切过的蚯蚓.每次要切时在这三堆里面选择最大的, 切完再依次放回去.   所以这么做时间复杂度为O(m)O(m).再优化一下细节基本上就没问题了.

结论: 善于发现题目中隐含的单调性.

Tip:有些细节需要仔细考虑不然会很惨

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 7100005
int n,m,k,t;
double u,v,p;
struct T
{
    int h,t;
    int q[N];
}q[4];
bool cmp(int a,int b)
{
    return a>b;
}
void push(int d,int x)
{
    q[d].t++;
    q[d].q[q[d].t]=x;
    //printf("%d %d\n",q.t,q.q[q.t]);
}
void pop(int d)
{
    q[d].h++;
}
int max()
{
    int d1=1,d2;
    d2=q[2].q[q[2].h]>q[3].q[q[3].h]?2:3;
    if(q[1].h>q[1].t)//注意有可能q[1]为空 后面是0 而q[2] q[3] 会出现负数
        return d2;
    d1=q[1].q[q[1].h]>q[d2].q[q[d2].h]?d1:d2;
    return d1;
}
int main(){
    //freopen("a.txt","r",stdin);
    //freopen("b.txt","w",stdout);
    q[1].h=1,q[1].t=0;
    q[2].h=1,q[2].t=0;
    q[3].h=1,q[3].t=0;
    scanf("%d %d %d %lf %lf %d",&n,&m,&k,&u,&v,&t);
    p=u/v;
    for(int i=0;i<n;++i)
    {
        int l;
        scanf("%d",&l);
        push(1,l);
    }
    sort(q[1].q+1,q[1].q+n+1,cmp);
    int ans=0;
    for(int i=1;i<=m;++i)
    {
        int d=max();
        int x=q[d].q[q[d].h]+ans;
        if(!(i%t))
            printf("%d ",x);
        pop(d);
        int a=floor(double(x)*p),b=x-a;
        //printf("%d %d %d\n",x,a,b);
        a-=ans,b-=ans;
        a-=k,b-=k;
        if(a>b)
        {
            push(2,a);
            push(3,b);
        }
        else
        {
            push(2,b);
            push(3,a);
        }
        ans+=k;
    }
    printf("\n");
    for(int i=q[2].h;i<=q[2].t;++i)
    {
        push(1,q[2].q[i]);
    }
    for(int i=q[3].h;i<=q[3].t;++i)
    {
        push(1,q[3].q[i]);
    }
    sort(q[1].q+q[1].h,q[1].q+q[1].t+1,cmp);
    int y=0;
    for(int i=q[1].h;i<=q[1].t;++i)
    {
        y++;
        if(!(y%t))
            printf("%d ",q[1].q[i]+ans);
        pop(1);
    }
    return 0;
}

加优化
三个堆都具有单调性,每一次比较三个堆顶就能得到最大值

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 7100005
int n,m,k,t;
double u,v,p;
struct T
{
    int h,t;
    int q[N];
}q[4];
bool cmp(int a,int b)
{
    return a>b;
}
void push(int d,int x)
{
    q[d].t++;
    q[d].q[q[d].t]=x;
    //printf("%d %d\n",q.t,q.q[q.t]);
}
void pop(int d)
{
    q[d].h++;
}
int max()//三个都要判空 七种情况 无比麻烦...
{
    int d1,d2,d3;
    d1=q[2].q[q[2].h]>q[3].q[q[3].h]?2:3;
    d2=q[1].q[q[1].h]>q[3].q[q[3].h]?1:3;
    d3=q[2].q[q[2].h]>q[1].q[q[1].h]?2:1;
    if(q[1].h>q[1].t)
    {
        if(q[2].h>q[2].t)//1 2为空
            return 3;
        if(q[3].h>q[3].t)//1 3为空
            return 2;
        return d1;//1为空
    }
    if(q[2].h>q[2].t)
    {
        if(q[3].h>q[3].t)//2 3为空
            return 1;
        return d2;//2为空
    }
    if(q[1].h>q[1].t)//3为空
        return d3;
    return q[d1].q[q[d1].h]>q[d2].q[q[d2].h]?d1:d2;//都不为空 可以任意两个比较
}
int main(){
    //freopen("a.txt","r",stdin);
    //freopen("b.txt","w",stdout);
    q[1].h=1,q[1].t=0;
    q[2].h=1,q[2].t=0;
    q[3].h=1,q[3].t=0;
    scanf("%d %d %d %lf %lf %d",&n,&m,&k,&u,&v,&t);
    p=u/v;
    for(int i=0;i<n;++i)
    {
        int l;
        scanf("%d",&l);
        push(1,l);
    }
    sort(q[1].q+1,q[1].q+n+1,cmp);
    int ans=0;
    for(int i=1;i<=m;++i)
    {
        int d=max();
        int x=q[d].q[q[d].h]+ans;
        if(!(i%t))
            printf("%d ",x);
        pop(d);
        int a=floor(double(x)*p),b=x-a;
        //printf("%d %d %d\n",x,a,b);
        a-=ans,b-=ans;
        a-=k,b-=k;
        if(a>b)
        {
            push(2,a);
            push(3,b);
        }
        else
        {
            push(2,b);
            push(3,a);
        }
        ans+=k;
    }
    printf("\n");
    for(int i=1;i<=m+n;++i)//直接利用单调性 找三个队列最大值并出队
    {
        int d=max();
        int x=q[d].q[q[d].h]+ans;
        pop(d);
        if(!(i%t))
            printf("%d ",x);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值