题目传送门(洛谷)
题目大意:有n条蚯蚓,每次把最长的切成两半,长度分别为⌊px⌋和x-⌊px⌋,其他的蚯蚓长度加q,重复m次,问某些时候切的蚯蚓的长度和最后的一些蚯蚓的长度。
十分显然的,这道题我们可以用堆轻易实现,用堆来维护最长的蚯蚓,拿出来切成两半然后再塞回堆里面去。顺手再输出一下,这道题就ok了,于是我无脑的打出了以下代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#define db double
int n,m,q,u,v,tt;
int t=0;
struct node{int x,y;};
node dui[8000010];
void swap(node &x,node &y){node pp=x;x=y;y=pp;}
int tot=0;
void up(int x)
{
while(x>1&&dui[x].x+q*(tot-dui[x].y-1)>dui[x/2].x+q*(tot-dui[x/2].y-1))
{
swap(dui[x],dui[x/2]);
x/=2;
}
}
void down(int x)
{
if(x>t/2)return;
int ans=x;
if(dui[ans].x+q*(tot-dui[ans].y-1)<dui[x*2].x+q*(tot-dui[x*2].y-1))ans=x*2;
if(x*2+1<=t&&dui[ans].x+q*(tot-dui[ans].y-1)<dui[x*2+1].x+q*(tot-dui[x*2+1].y-1))ans=x*2+1;
if(ans!=x)
{
swap(dui[x],dui[ans]);
down(ans);
}
}
void add(int x,int y)
{
dui[++t].x=x;
dui[t].y=y;
up(t);
}
node pop()
{
node re=dui[1];
dui[1]=dui[t--];
down(1);
return re;
}
int main()
{
scanf("%d %d %d %d %d %d",&n,&m,&q,&u,&v,&tt);
db p=(db)u/(db)v;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
add(x,0);
}
while(m--)
{
node x=pop();
tot++;
if(tot%tt==0)printf("%d ",x.x+q*(tot-x.y-1));
add((int)floor((db)(x.x+q*(tot-x.y-1))*p),tot);
add((int)((db)(x.x+q*(tot-x.y-1))-floor((db)(x.x+q*(tot-x.y-1))*p)),tot);
}
printf("\n");
int total=0;
tot++;
while(t)
{
total++;
node x=pop();
if(total%tt==0)printf("%d ",x.x+q*(tot-x.y-1));
}
}
一交上去,65分,顿时就懵逼了,翻翻讨论发现这题貌似需要卡常,于是又加上了读优以及一些硬核优化,于是。。变成了75分。。。
伤感的提交记录:
然后,就只能想想其他做法了。(正文开始)
想一想刚刚的堆的时间复杂度,O(mlogn),看起来十分的优秀,仔细一想m是7*10^6,n是10^5,这个时间复杂度还是不怎么优的,于是,显然的,正解一定是O(m)的!
那么一想到每一次需要做到O(1)查询最大值,毫无疑问一定是用优先队列了(不是堆),于是考虑将一开始的蚯蚓排序,每次取第一个(也就是最大的),把它切了塞回队列里面。
相信聪明的你一定发现了问题——1、如何做到将其他的蚯蚓的长度+q ; 2、塞回队列的时间是O(n)
对于问题1,我们可以给每一只蚯蚓一个时间戳,记录他是什么时候进入队列的,那么每一次用 原来的长度 + (现在的时间-进队列的时间)*q 便可以求出它现在的长度,具体实现方法可以参考上面的堆代码。
对于问题2,我们发现,不会优化。qaq
于是发现把蚯蚓切成两半后是不能塞回队列的,那么,这个问题就转化成了:如何处置这两只蚯蚓?
经过一番模拟后,发现每次被切出来的两只蚯蚓,其实长度是单调不上升的,比如我在时刻1切了一只蚯蚓,切成x1和x2,时刻2又切了一只蚯蚓,切成y1和y2,同时,x1+q,x2+q,然后一做对比,此时的x1y1,并且x2
y2。为啥?这里给出一种可能比较通俗易懂的
证明:
我们设在有两只蚯蚓 f1,f2 分别在t1和t2时刻被切成两半。(t1<t2)
因为我们每次切的都是最长的蚯蚓,所以在t1时刻,f1f2
我们把f1切成x1和x2后,在t1+1~t2这段时间,每次都是x1+q,x2+q,到了t2时刻的时候,x1变成了x1+(t2-t1-1)*q,x2变成了x2+(t2-t1-1)*q。
为了简化这个式子以及方便理解,我们设t2~t1+1这段时间一共有tc个单位时间,也就是tc=t2-t1-1,以及将t2时刻的x1、x2和f2写为x1' , x2' , f2'
于是式子变成了x1'=x1+tc*q,x2'=x2+tc*q
以及f2'=f2+tc*q
在t2时刻,我们将f2'切成了y1和y2,故y1=⌊p * f2'⌋=⌊p * (f2+tc*q)⌋ , y2=f2' - ⌊p * f2'⌋=(f2+tc*q) - ⌊p * (f2+tc*q)⌋
顺便写出x1和x2——x1=⌊p * f1⌋ , x2=f1 - ⌊p * f1⌋
将x1和x2带入x1'和x2'中,得到x1'=⌊p * f1⌋+tc*q,x2'=f1 - ⌊p * f1⌋+tc*q
于是我们先比较一下x1'和y1
将x1'和y1整理一下就变成了
x1'=⌊p * f1⌋+tc*q=⌊p * f1+tc*q⌋
y1=⌊p * (f2+tc*q)⌋=⌊p * f2+p*tc*q)⌋(下面为了方便用一下LaTeX
比较x2'和y2的过程类似,就不细讲了(有兴趣的话可以自己手推一下),由此"x1y1,并且x2
y2"
得证
那么这道题的思路就很明显了,开三个队列,对于第一个队列,存排好序的初始蚯蚓,第二个队列存被切出来的长度为⌊px⌋的蚯蚓,第三个队列存长度为x-⌊px⌋的蚯蚓,每次找出每条队列中长度最大的蚯蚓进行比较,得到在所有蚯蚓中长度最大的那只,拿出来切开再塞回第二第三条队列就好了。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,q,u,v,t,tot=0;
double p;
struct node{
int x,y;
int kk(){return x!=0?(x+(tot-y-1)*q):0;}//计算当前长度,因为我调用kk的时候都是在当前时间蚯蚓的长度还没+q的时候,所以tot-y-1,还有如果x=0说明这个位置其实是没有蚯蚓的,所以要返回0
}a[100010],b[7000010],c[7000010];
int sta=1,stb=1,stc=1,&eda=n,edb=0,edc=0;
bool cmp(node x,node y){return x.x>y.x;}
void work(node *f,int &st)
{
if(tot%t==0)printf("%d ",f[st].kk());//满足要求就输出
b[++edb].x=(int)__builtin_floor((double)f[st].kk()*p);//__builtin_floor是向下取整,注意不能直接强行转成int
b[edb].y=tot;//记录进队列时间
c[++edc].x=f[st].kk()-(int)__builtin_floor((double)f[st].kk()*p);
c[edc].y=tot;
st++;
}
int main()
{
scanf("%d %d %d %d %d %d",&n,&m,&q,&u,&v,&t);
p=(double)u/(double)v;
for(int i=1;i<=n;i++)
scanf("%d",&a[i].x),a[i].y=tot;
sort(a+1,a+n+1,cmp);
while(m--)
{
tot++;
if(a[sta].kk()>=b[stb].kk()&&a[sta].kk()>=c[stc].kk())work(a,sta);//找出长度最长的蚯蚓
else if(b[stb].kk()>=a[sta].kk()&&b[stb].kk()>=c[stc].kk())work(b,stb);
else work(c,stc);
}
printf("\n");
tot++;
int total=0;
while(sta<=eda||stb<=edb||stc<=edc)
{
total++;//按长度依次弹出所有蚯蚓并输出
if(a[sta].kk()>=b[stb].kk()&&a[sta].kk()>=c[stc].kk())(total%t==0?printf("%d ",a[sta++].kk()):sta++);
else if(b[stb].kk()>=a[sta].kk()&&b[stb].kk()>=c[stc].kk())(total%t==0?printf("%d ",b[stb++].kk()):stb++);
else (total%t==0?printf("%d ",c[stc++].kk()):stc++);
}
}