题目链接https://codeforces.com/gym/100257
题意
给出一个递推式
x
i
=
(
A
⋅
x
i
−
2
+
B
⋅
x
i
−
1
+
C
)
m
o
d
2
31
x_i = (A · x_{i−2} + B · x_{i−1} + C) mod 2^{31}
xi=(A⋅xi−2+B⋅xi−1+C)mod231,问你求出前
n
n
n项的前
k
k
k大。
1
≤
n
≤
1
e
8
,
1
≤
k
≤
2
e
5
1\leq n \leq 1e8, 1\leq k\leq 2e5
1≤n≤1e8,1≤k≤2e5
题解一
队友的神仙想法:把值域为
2
31
2^{31}
231的以
10000
10000
10000为一个块长,分成大概
2
e
5
2e5
2e5个块,扫一遍递推式,累加各个块内值的个数。然后从后往前做一遍后缀和,就能够知道第
k
k
k大是落在哪个块内。再对这个块内的做一遍后缀和,就可以知道确切的第
k
k
k大是多少。知道第
k
k
k大之后,就只要再扫一遍递推式,就可以输出答案了。
不同的写法可能会是三倍或四倍的
O
(
n
)
O(n)
O(n),写的不好看了可能会TLE (比如说我)。其实这题模数非常特殊:
2
31
2^{31}
231,正好是int的大小,所以可以先用unsigned int保存让它自然溢出,最后与mod-1与一下就行了。这样子操作就会非常非常快,写丑了也没关系。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned int ll;
const int N=3e5+7;
const ll mod=(((ll)1)<<31)-1;
ll ar[N];
ll n,k;
ll x,y,xx,yy,a,b,c;
ll tot;
int main(){
scanf("%u%u",&n,&k);
scanf("%u%u%u%u%u",&x,&y,&a,&b,&c);
ll ma=0;
xx=x;yy=y;
for(ll i=1;i<=n;i++){
ll now=(xx*a+yy*b+c)&mod;
ar[now/10000]++;
xx=yy;yy=now;
ma=max(ma,now);
}
ll p,num;
for(ll i=ma/10000;i>=0;i--){
ar[i]=ar[i]+ar[i+1];
if(ar[i]>=k){
num=ar[i+1];
p=i;
break;
}
}
for(ll i=0;i<N;i++) ar[i]=0;
ll le=p*10000;
ll ri=le+10000-1;
xx=x;yy=y;
for(ll i=1;i<=n;i++){
ll now=(xx*a+yy*b+c)&mod;
xx=yy;yy=now;
if(now/10000==p){
ar[now-le]++;
}
}
ll lim=0;
for(ll i=10000;i>=0;i--){
ar[i]=ar[i]+ar[i+1];
if(ar[i]>=k-num){
lim=le+i;
break;
}
}
tot=0;
xx=x;yy=y;
for(ll i=1;i<=n;i++){
ll now=(xx*a+yy*b+c)&mod;
xx=yy;yy=now;
if(now>lim) ar[++tot]=now;
}
sort(ar+1,ar+1+tot,greater<ll>());
bool fst=true;
for(ll i=1;i<=tot;i++){
if(fst) fst=false;
else printf(" ");
printf("%d",ar[i]);
}
while(tot<k){
if(fst) fst=false;
else printf(" ");
printf("%d",lim);
tot++;
}
}
题解二
还有一个神仙做法是用nth_element。这是STL的一个函数,能够 O ( n ) O(n) O(n)地求出第 k k k大。这样就只要维护一个 1 e 6 1e6 1e6左右的数组,每次算出的数如果大于第 k k k大就加入数组,一旦数组存不下了就重新计算出当前第 k k k大,并把数组大小设为 k k k也就是 2 e 5 2e5 2e5。这样差不多只会计算几百次第 k k k大,复杂度也是 O ( n ) O(n) O(n)级别。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned int ll;
const int N=1e6+7;
const ll mod=(((ll)1)<<31)-1;
ll ar[N];
ll n,k,x,y,a,b,c,xx,yy,tot,kth;
int main(){
scanf("%u%u",&n,&k);
scanf("%u%u%u%u%u",&x,&y,&a,&b,&c);
xx=x;yy=y;
for(ll i=1;i<=n;i++){
ll now=(xx*a+yy*b+c)&mod;
if(now>kth){
ar[++tot]=now;
if(tot==N-1){
nth_element(ar+1,ar+1+k-1,ar+1+tot,greater<ll>());
kth=ar[k];
tot=k;
}
}
xx=yy;yy=now;
}
nth_element(ar+1,ar+1+k-1,ar+1+tot,greater<ll>());
kth=ar[k];
xx=x;yy=y;
tot=0;
for(ll i=1;i<=n;i++){
ll now=(xx*a+yy*b+c)&mod;
if(now>kth) ar[++tot]=now;
xx=yy;yy=now;
}
sort(ar+1,ar+1+tot,greater<ll>());
bool fst=true;
for(ll i=1;i<=tot;i++){
if(fst) fst=false;
else printf(" ");
printf("%u",ar[i]);
}
while(tot<k){
if(fst) fst=false;
else printf(" ");
printf("%u",kth);
tot++;
}
}