「NOIP模拟赛 贰」Median题解

题面简述

定义两个数列\(S = \{S(1), S(2), ..., S(n)\}\)\(S_2 = \{S_2(1), S_2(2), ..., S_2(n)\}\)

\[S(k) = (p_k\times k) \bmod w,\text{where } p_k \text{ is the kth prime number}\]

\[S_2(k) = S(k) + S(\lfloor\frac{k}{10}\rfloor + 1)\]

\(M(i,j)\)表示\(S_2(i)\)\(S_2(j)\)的中位数。现在给定\(n,k\)
\[\sum_{i=1}^{n-k+1} M(i, i + k - 1)\]

\(w\leq k\leq n \leq 10^7\)

col

我们先用线性筛筛出素数,然后就可以构造出\(S1\)\(S2\)了,我们发现S2数组没什么规律,也就是说可以看做是随机构造的。在一个滑动窗口中随机构造的数的中位数变化很小,于是我们可以用两个指针控制住中位数,就可以求解了。因为数的范围很小,我们就可以用计数的方法来找中位数。

code

#include<bits/stdc++.h>
using namespace std;
int n,k,w,len,P[maxn],S[maxn],S2[maxn],cnt[maxn*2],p1=-1,p2=-1,cur1,cur2;bool vis[179424678];double ans;
inline int read(){
    int ret=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
    while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
inline void make_p(){
    for(int i=2;;i+=(i&1)+1){
        if(!vis[i]){P[++plen]=i;if(plen==n) break;}
        for(int j=1;j<=plen&&i*P[j]<=179424673;j++){
            vis[i*P[j]]=true;
            if(i%P[j]==0) break;
        }
    }
}
int main(){
    n=read();k=read();w=read();
    make_p();
    for(int i=1;i<=n;i++) S[i]=(long long)P[i]*i%w;
    for(int i=1;i<=n;i++)S2[i]=S[i]+S[i/10+1];
    for(int i=1;i<=k;i++) cnt[S2[i]]++;
    for(int i=1;i<=n-k+1;i++)
    {
        while(cur1<k/2) cur1+=cnt[++p1];
        while(cur1-cnt[p1]>=k/2) cur1-=cnt[p1--];
        while(cur2<k/2+1) cur2+=cnt[++p2];
        while(cur2-cnt[p2]>=k/2+1) cur2-=cnt[p2--];
        if(k&1) ans+=p2;
        else ans+=p1+p2;
        if(i<n-k+1){
            cnt[S2[i]]--;
            if(S2[i]<=p1) cur1--;
            if(S2[i]<=p2) cur2--;
            cnt[S2[k+i]]++;
            if(S2[k+i]<=p1) cur1++;
            if(S2[k+i]<=p2) cur2++;
        }
    }
    printf("%.1lf",k&1?ans:ans/2);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/booksBlog/p/12222168.html