给出一个大小为n集合({a_i}),每次操作选择集合中最大的元素(a_i),将其从集合中删除,然后集合中所有的元素值加上q,再向集合中加入元素([pa_i])和(a_i-[pa_i]),显然p,q已经给出,给出m,t,询问第(t,2t,...,[m/t])次操作前中集合中最大的元素,并且输出所有操作完以后集合中第(t,2t,...,[(n+m)/t])大的元素。
(1≤n≤10^5,0≤ai≤10^8,0<p<1,0≤q≤200)
(0≤m≤7 imes 10^6,1≤t≤71)
解
法一:优先队列
注意到集合中每次要选出最大的元素,可以考虑优先队列维护,弹出队首(a_i),然后将其变成元素([pa_i])和(a_i-[pa_i])加入优先队列,按照题目条件每次输出队首即可。
至于集合中的每个元素都增加一个q,全局中开一个变量保存增加的值(不能理解没关系,具体实现看代码)。
而最后的时候不停地弹出队首,这是第几次弹出队首,队首就是第几大元素,按照指定的要求输出第k大的元素。
最终时间复杂度((n+m)log(n+m)),超时。
参考代码:
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <functional>
#define il inline
#define ri register
#define lb double
using namespace std;
priority_queue<int,vector<int>,less<int> >Q;
void pen(int);
il void read(int&);
int main(){
int n,m,q,u,v,t;lb p;
read(n),read(m),read(q),
read(u),read(v),read(t);p=(lb)u/v;
ri int i,j,gzy(0);for(i=1;i<=n;++i)read(j),Q.push(j);
for(i=1;i<=m;++i){j=Q.top()+gzy,Q.pop();
if(!(i%t))pen(j),putchar(' ');gzy+=q;
Q.push((int)(j*p)-gzy),Q.push(j-(int)(j*p)-gzy);
}putchar('
');
for(i=1;i<=n+m;++i){
if(!(i%t))pen(Q.top()+gzy),putchar(' ');Q.pop();
}
return 0;
}
void pen(int x){
if(x>9)pen(x/10);putchar(x%10+48);
}
il void read(int &x){
x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
法二:单调性+归并排序
一个很隐晦的性质,也许猜问题具有单调性会得到,某次操作下,设集合中两个元素(a_igeq a_j),此时(a_i)被选出用于操作变为元素(pa_i,(1-p)a_i),经过(t')次操作后,如果这两个元素没有被操作,将变为(pa_i+qt',(1-p)a_i+qt'),如果恰好(j)被操作了,那么就会分成两个元素(p(a_j+t'q)=pa_j+pt'q,(1-p)(a_j+t'q)=(1-p)a_j+(1-p)tq'),因为(a_igeq a_j,pin(0,1),1-pin(0,1)),得到
于是对于操作分出的元素,按照其在操作前的元素大小,本身具有单调性,如果没理解这句话,继续后看。
于是我们维护3个队列,第1个队列存储初始的集合,但是要从大到小排序,第二个队列存储每次操作(假设操作的是(a_i))产生(pa_i),而第三个队列存储((1-p)a_i),在当前操作的集合中,后面的被操作的元素,肯定小于等于当前被操作的元素,根据性质所分出来的两个元素乘以(p)的会单调递减,乘以(1-p)会单调递减,于是只要分别把其入应该入的队列,就可以保证这个队列的单调性,于是三个队列都能保证其单调递减,因此只要取各自的队首就可以知道当前操作下集合中最大的元素。
对于最后的询问,因为三个队列都是有序的,显然多个有序的数列排序,考虑归并,最终可以做到时间复杂度(O(n+m))。
参考代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <functional>
#define il inline
#define ri register
#define Size 7200000
using namespace std;
int T[3][Size],L[3],R[3],
te[Size],tt;
il void read(int&);
int main(){int n,m,q,u,v,t;ri int gzy(0);
ri double p;read(n),read(m),read(q);
read(u),read(v),read(t),p=(double)u/v;
for(int i(1);i<=n;++i)read(T[0][i]);
sort(T[0]+1,T[0]+n+1,greater<int>());
L[0]=1,R[0]=n,L[1]=L[2]=1;
for(ri int i(1),j,k;i<=m;++i){j^=j;
if(T[1][L[1]]>T[j][L[j]]||L[0]>R[0])j=1;
if(T[2][L[2]]>T[j][L[j]])j=2;
k=T[j][L[j]++]+gzy,gzy+=q;
T[1][++R[1]]=(int)(k*p)-gzy;
T[2][++R[2]]=k-(int)(k*p)-gzy;
if(!(i%t))printf("%d ",k);
}putchar('
');
while(L[0]<=R[0]&&L[1]<=R[1])
if(T[0][L[0]]>T[1][L[1]])te[++tt]=T[0][L[0]++];
else te[++tt]=T[1][L[1]++];
while(L[0]<=R[0])te[++tt]=T[0][L[0]++];
while(L[1]<=R[1])te[++tt]=T[1][L[1]++];
L[0]=1,R[0]=tt,R[1]=0;
while(L[0]<=R[0]&&L[2]<=R[2])
if(te[L[0]]>T[2][L[2]])T[1][++R[1]]=te[L[0]++];
else T[1][++R[1]]=T[2][L[2]++];
while(L[0]<=R[0])T[1][++R[1]]=te[L[0]++];
while(L[2]<=R[2])T[1][++R[1]]=T[2][L[2]++];
for(ri int i(1);i<=R[1];++i)
if(!(i%t))printf("%d ",T[1][i]+gzy);
return 0;
}
il void read(int &x){
x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}