1 条题解

  • 0
    @ 2026-9-23 23:19:01

    这是道偏乱搞的题。

    当 nn 较小时,直接线性筛出 nn 以内的素数,然后双指针求解即可,复杂度 O(n)O(n)。

    但是 n≤1011n\le 10^{11} 时显然不能这样做,这时我们先处理出一个阈值 BB 之内的素数,然后双指针求解。

    其实这样就可以水过大部分测试点了。

    注意下面的“∼\sim”符号有时表示等价关系(两者比值趋于 11),有时表示一个区间,请注意区分。

    如果未找到解,考虑其它情况,可以证明素数个数为 O(nB)O(\frac nB)。怎么证明呢?我们尝试使素数个数尽量多,考虑所有素数的平均数,我们使之尽量小,但是可能的解的区间右端点必然在 BB 右侧(否则刚才已经求出来了),能使素数平均数最小的区间显然就是 [2,B][2,B] 了(就是刚刚到 BB 的区间),这时素数的平均数为(根据经典数论结论)

    $$\frac{\sum_{2\le p\le B}p}{\sum_{2\le p\le B}1}\sim\frac{B^2\ln B}{B\ln B}\sim B$$

    因为这是素数平均数最小的情况,而素数和为 nn,所以素数个数的最大值就是 O(nB)O(\frac nB) 了,常数趋于 11(也就是 素数个数最大值∼nB素数个数最大值\sim\frac nB),直接枚举 1∼2nB1\sim2\frac nB 稳过。

    若枚举到有 kk 个素数,则其平均数必为 nk\frac nk,则每个素数都在其附近,考虑素数密度,根据素数定理,在 nk\frac nk 附近时大约平均每 ln⁡nk\ln\frac nk 个数有一个素数,而一共有 kk 个,故所有素数都是 nk+O(kln⁡nk)\frac nk+O(k\ln\frac nk) 范围的,这个范围只有 O(kln⁡nk)O(k\ln\frac nk) 个数,判断出其中的所有素数并双指针搜索即可,注意由于题目数据较为毒瘤,故意取素数密度较低的位置,区间大小的常数需放大一点。

    对于一个长度为 xx 的区间,设数值值域为 ss(也就是区间内的数最大值大概是 O(s)O(s)),每一个数都试除法判断是否为素数,只用试除已经处理出的素数(先预处理出 O(s)O(\sqrt s) 以内的素数),则均摊复杂度为 O(xs14)O(xs^{\frac14}) 别问我是怎么得出来的。实测快于米勒 - 拉宾素性测试,但仍然会超时。

    所以说我们直接用埃氏筛代替就行了。那我刚才讲那个干什么呢?还不是因为我死在那调了一个小时……

    故总复杂度为 O(B+n2B2log⁡nlog⁡log⁡n)O(B+\frac{n^2}{B^2}\log n\log\log n),理论上调整 BB 的大小可以做到 O(n23(log⁡nlog⁡log⁡n)13)O(n^{\frac23} (\log n\log\log n)^{\frac13})。

    但是这看起来也不是很能过啊。反正实际上就是过了。

    实际做题的时候,我们不需要卡那么准,BB 的大小使线性筛的 bitset\text{bitset} 不爆空间就行了,直接开到 5×1075\times10^7 即可。

    代码(有点紧凑,将就着看吧)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll n,b,p[12000000],c,su[12000000],ip,q[100000],qb,qf;
    bitset<50000005>bs;
    bitset<100000>ik;
    inline ll mi(ll x,ll y){
    	return x<y?x:y;
    }inline bool is(ll x){
    	for(ll j=1;p[j]*p[j]<=x;j++){
    		if(x%p[j]==0)return 0;
    	}return 1;
    }int main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0),cout.tie(0);
    	cin>>n;
    	b=mi(5e7,n);
    	for(ll i=2;i<=b;i++){
    		if(!bs[i]){
    			p[++c]=i;
    			su[c]=su[c-1]+i;
    		}for(ll j=1;p[j]*i<=b;j++){
    			bs[p[j]*i]=1;
    			if(i%p[j]==0)break;
    		}
    	}for(ll i=0;i<c;i++){
    		while(su[ip]-su[i]<n){
    			ip++;
    			if(ip>c)break;
    		}if(su[ip]-su[i]==n){
    			cout<<p[i+1]<<' '<<p[ip];
    			return 0;
    		}
    	}if(n==b){
    		cout<<"NIE";
    		return 0;
    	}for(ll i=1;i<=n*2/b;i++){
    		qb=1;
    		qf=0;
    		ll j=n/i-2.5*i*log(n/i),l=j,r=j-1,x=0,rr=n*2/i-j,sm=0;
    		ik.reset();
    		for(ll ii=1;p[ii]*p[ii]<=rr;ii++){
    			for(ll jj=(l+p[ii]-1)/p[ii]*p[ii];jj<=rr;jj+=p[ii]){
    				ik[jj-l]=1;
    			}
    		}
    		while(x<i){
    			r++;
    			if(!ik[r-l]){
    				x++;
    				sm+=r;
    				q[++qf]=r;
    			}
    		}while(sm<n){
    			j=q[qb]+1;
    			sm-=q[qb];
    			qb++;
    			while(1){
    				r++;
    				if(!ik[r-l]){
    					x++;
    					sm+=r;
    					q[++qf]=r;
    					break;
    				}
    			}
    		}if(sm==n){
    			cout<<q[qb]<<' '<<q[qf];
    			return 0;
    		}
    	}cout<<"NIE";
    	return 0;
    }
    
    • 1

    [POI 2020/2021 R3] 素数和 / Suma liczb pierwszych

    信息

    ID
    8987
    时间
    15000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者