前言
早年一直拿堆过不了,结果发现要用队列
正题
题目链接:https://www.luogu.com.cn/problem/P2827
题目大意
有 条蚯蚓,每次选取最长的一条,切成 和 的两段,然后其余蚯蚓变长 。
求每次切开蚯蚓的长度和最后所以蚯蚓的长度。
解题思路
先不考虑变长
和合并果子的思路很像,我们发现如果从大到小切切出来的也是从大到小的,所以我们开三个队列,第一个队列装初始的蚯蚓,然后切出来的左边装到第二个队列,右边装到第三个队列,这样每个队列的长度也是单调的,之后每次取出三个队头里最长的来剪掉就好了。
考虑变长,初始的蚯蚓第 次长度为 ,那么我们直接当做所有长度都是 的蚯蚓,然后将第 次加入的蚯蚓长度变为 即可。
时间复杂度
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=7e6+10;
ll n,m,q,u,v,t,a[N],q1[N*2],q2[N*2],q3[N*2];
int main()
{
scanf("%lld%lld%lld%lld%lld%lld",&n,&m,&q,&u,&v,&t);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
sort(a+1,a+1+n);
memset(q1,-127,sizeof(q1));
memset(q2,-127,sizeof(q2));
memset(q3,-127,sizeof(q3));
ll h1,h2,h3,t1,t2,t3;
h1=h2=h3=1;t1=t2=t3=0;
for(ll i=n;i>=1;i--)
q1[++t1]=a[i];
for(ll i=1;i<=m;i++){
ll l,r,x;
if(q1[h1]>=q2[h2]&&q1[h1]>=q3[h3])x=q1[h1++];
else if(q2[h2]>=q1[h1]&&q2[h2]>=q3[h3])x=q2[h2++];
else x=q3[h3++];
x+=(i-1)*q;l=x*u/v;r=x-l;
q2[++t2]=l-q*i;q3[++t3]=r-q*i;
if(i%t==0) printf("%lld ",x);
}
printf("\n");
for(ll i=1;i<=m+n;i++){
ll x;
if(q1[h1]>=q2[h2]&&q1[h1]>=q3[h3])x=q1[h1++];
else if(q2[h2]>=q1[h1]&&q2[h2]>=q3[h3])x=q2[h2++];
else x=q3[h3++];
if(i%t==0)printf("%lld ",x+m*q);
}
}
附上远古时期堆的代码
#include<cstdio>
#include<algorithm>
#define N 100010
#define M 7000010
using namespace std;
int n,a[N+M],num,m,q,t,addl,u,v;
double p;
void up(int x)
{
while(x>1&&a[x>>1]<a[x])
{
swap(a[x],a[x>>1]);
x>>=1;
}
}
void down(int x)
{
int y;
while((x<<1)<=num&&a[x<<1]>a[x]
||((x<<1)|1)<=num&&a[(x<<1)|1]>a[x])
{
y=x<<1;
if(a[y]<a[y|1]&&((x<<1)|1)<=num) y|=1;
swap(a[x],a[y]);
x=y;
}
}
int main()
{
scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
p=(double)u/v;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
num++;
up(num);
}
int k=1;
for (int i=1;i<=m;i++)
{
int k1,k2;
a[1]=a[1]+addl;
k1=(double)a[1]*p;
k2=a[1]-k1;
if(k==t)
{printf("%d ",a[1]);k=0;}
a[1]=k1-addl-q;down(1);
a[++num]=k2-addl-q;
k++;
up(num);
addl+=q;
}
k=1;
printf("\n");
while(num)
{
if(k==t) printf("%d ",a[1]+addl),k=0;
swap(a[1],a[num]);
num--;
k++;
down(1);
}
}