一般思路:
一般做法:维护一个大根堆,每次都去除堆顶元素x,
还有要维护一个偏移量Δx,取出的元素x+Δx才是当前的真实最大值,
1.令x=x+Δx
2.把 ⌊ px⌋-Δx-q与x- ⌊ px⌋-Δx-q加入大根堆。
3.令Δx=Δx+q
这样子一共做m次,每次都是O(logn)的时间,一共是O(mlogn),会超时。
优化思路:
对原序列a[N]从大到小排序,放入一个队列a中
设每次取出来a的队头分解后的两个数
一部分是left= ⌊ px⌋-Δx-q,一部分是right=x- ⌊ px⌋-Δx-q。
left和right不再放回a中,另外开两个队列存储相应部分
一共构造三个队列a,left,right,
证明每一次最大值一定是在这三个队列的队头之中产生。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int N=1e5+10,M=7e6+10;
int n,m,q,p,u,v,t;
int a[N],l[M],r[M];
int hh1,hh2,hh3,tt1=-1,tt2=-1,tt3=-1;
int delta;
int get_max()
{
int x=-1e9;
if(hh1<=tt1) x=max(x,a[hh1]);
if(hh2<=tt2) x=max(x,l[hh2]);
if(hh3<=tt3) x=max(x,r[hh3]);
if(hh1<=tt1&&x==a[hh1]) hh1++;
else if(hh2<=tt2&&x==l[hh2]) hh2++;
else hh3++;
return x;
}
int main()
{
cin>>n>>m>>q>>u>>v>>t;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
sort(a,a+n);
reverse(a,a+n);//从大到小
tt1=n-1;
for(int i=1;i<=m;i++)
{
int x=get_max();
x+=delta;
int l1=x*1ll*u/v;
int r1=x-l1;
if(i%t==0) printf("%d ",x);
delta+=q;
l[++tt2]=l1-delta;
r[++tt3]=r1-delta;
}
cout<<endl;
for(int i=1;i<=m+n;i++)
{
int x=get_max();
if(i%t==0)
printf("%d ",x+delta);
}
cout<<endl;
return 0;
}