Median——题解

题目大意

定义两个数列:S={S1,S2Sn}S={S1,S2……Sn}S2={S21,S22,S2n}S2={S21,S22,……S2n}
其中
Si=(Pii)mod  WSi=(Pi∗i)mod  WPiPi表示第ii个素数)
S2i=Si+Si/10+1

nk+1i=1M(i,i+k1)∑i=1n−k+1M(i,i+k−1)M(L,R)M(L,R)表示S2[L,R]S2[L,R]的中位数)

n,k,W<=10000000,2sn,k,W<=10000000,2s

首先,筛出第1000000010000000个素数?素数的分布是近似loglog的,所以第1000000010000000个素数应近似为200000000200000000(实际为179424673179424673),此时loglog级的筛素数都吃不消了,只能上高端线性欧拉筛+各种卡常:(然而还是要1.5s1.5s!!!

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;
        }
    }
}

然后,O(1)O(1)求区间中位数?
loglog的做法有很多,写大小两个堆或直接上平衡树,但0.5s0.5s你想干嘛。。
然后玄学的来了。。

注意到这题的输入很诡异,其实可以当做是随机数据 那么随机数据满足分布均匀,也就是说可以假定,中位数的值变化也是常数级的
那么只要维护一个指针,挪动更新下中位数的位置就好了 ——摘自SolutionSolution

每次区间移动只更改两个元素,看起来也挺有道理的吧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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值