题目大意
定义两个数列:
,
其中
(
表示第
个素数)
求
(
表示
的中位数)
首先,筛出第
个素数?素数的分布是近似
的,所以第
个素数应近似为
(实际为
),此时
级的筛素数都吃不消了,只能上高端线性欧拉筛+各种卡常:(然而还是要
!!!)
void make(){
for(__R int i=2;i<=N;i+=(i&1)+1){
if(!vis[i]){p[++p[0]]=i;if(p[0]==n) return;}
for(__R int j=1;j<=p[0];j++){
if(p[j]>N/i) break;
vis[p[j]*i]=1;
if(!(i%p[j])) break;
}
}
}
然后,
求区间中位数?
的做法有很多,写大小两个堆或直接上平衡树,但
你想干嘛。。
然后玄学的来了。。
“注意到这题的输入很诡异,其实可以当做是随机数据
那么随机数据满足分布均匀,也就是说可以假定,中位数的值变化也是常数级的
那么只要维护一个指针,挪动更新下中位数的位置就好了” ——摘自
每次区间移动只更改两个元素,看起来也挺有道理的吧QWQ
然后就是写起来太像玄学的莫队了。。
#pragma GCC optimize(2)
#include<cstdio>
#include<algorithm>
#define __R register
#define LL long long
using namespace std;
const int maxp=(1e7)+5,maxn=179424680,N=179424673;
int n,k,TT,p[maxp],C[maxp],A[maxp],hsh[maxp<<1];bool vis[maxn];LL Ans;
int Q;
void make(){
for(__R int i=2;i<=N;i+=(i&1)+1){
if(!vis[i]){p[++p[0]]=i;if(p[0]==n) return;}
for(__R int j=1;j<=p[0];j++){
if(p[j]>N/i) break;
vis[p[j]*i]=1;
if(!(i%p[j])) break;
}
}
}
int main(){
scanf("%d%d%d",&n,&k,&TT),make();
for(__R int i=1;i<=n;i++) C[i]=(LL)p[i]*i%TT,A[i]=C[i]+C[i/10+1];
for(__R int i=1;i<k;i++) hsh[A[i]]++;
int b1=-1,b2=-1,lst1=0,lst2=0,kk=k+1>>1;bool flg=(k&1);
for(__R int i=k;i<=n;i++){
hsh[A[i]]++,lst1+=(A[i]<=b1),lst2+=(A[i]<=b2);
if(i>k) hsh[A[i-k]]--,lst1-=(A[i-k]<=b1),lst2-=(A[i-k]<=b2);
while(lst1<kk) lst1+=hsh[++b1];
while(lst2<kk+1) lst2+=hsh[++b2];
while(lst1-hsh[b1]>=kk) lst1-=hsh[b1--];
while(lst2-hsh[b2]>=kk+1) lst2-=hsh[b2--];
if(flg) Ans+=b1+b1;else Ans+=b1+b2;
}
if(Ans&1) printf("%lld.5\n",Ans>>1);else printf("%lld.0\n",Ans>>1);
return 0;
}