2021ccpc威海 I.Distance(min25筛)

传送门:https://codeforces.com/gym/103428/problem/I题目大意:给定一个数nnn,问你111到nnn的整除关系的哈斯图上,相互两点的最短距离和是多少,定义两点i,j(i>j)i,j(i>j)i,j(i>j)之间的边权是ij\frac{i}{j}ji​。题解:如果不懂整除关系的哈斯图是什么样子可以看看下图。不难发现,在图上的每一步行走,相当于乘上一个质数ppp,或者除以一个质数ppp。那么对于i=pi1si1pi2si2⋯pirsir,j=pj
摘要由CSDN通过智能技术生成

传送门:https://codeforces.com/gym/103428/problem/I
题目大意:给定一个数 n n n,问你 1 1 1 n n n的整除关系的哈斯图上,相互两点的最短距离和是多少,定义两点 i , j ( i > j ) i,j(i>j) i,j(i>j)之间的边权是 i j \frac{i}{j} ji
题解:如果不懂整除关系的哈斯图是什么样子可以看看下图。
https://img-blog.csdnimg.cn/20201013231743708.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hhbjEyMDIwMTI=,size_16,color_FFFFFF,t_70#pic_center
不难发现,在图上的每一步行走,相当于乘上一个质数 p p p,或者除以一个质数 p p p。那么对于 i = p i 1 s i 1 p i 2 s i 2 ⋯ p i r s i r , j = p j 1 s j 1 p j 2 s j 2 ⋯ p j r s j r i=p_{i1}^{s_{i1}}p_{i2}^{s_{i2}}\cdots p_{ir}^{s_{ir}},j=p_{j1}^{s_{j1}}p_{j2}^{s_{j2}}\cdots p_{jr}^{s_{jr}} i=pi1si1pi2si2pirsir,j=pj1sj1pj2sj2pjrsjr,其两点的最短距离一定是
∑ p ∈ p r i m e p × ∣ s i − s j ∣ \sum\limits_{p\in prime}p\times \left|s_i-s_j\right| pprimep×sisj
稍加变换一下,可以得到
∑ p ∈ p r i m e ∑ i = 1 p i ≤ n ∑ j = 0 i − 1 2 p ( i − j ) × CNT ⁡ n ( p i ) × CNT ⁡ n ( p j ) \begin{aligned} &\sum\limits_{p\in prime}\sum\limits_{i=1}^{p^i\leq n}\sum\limits_{j=0}^{i-1}2p(i-j)\times \operatorname{CNT}_n(p^i)\times \operatorname{CNT}_n(p^j) \end{aligned} pprimei=1pinj=0i12p(ij)×CNTn(pi)×CNTn(pj)
其中定义 CNT ⁡ n ( p k ) \operatorname{CNT}_n(p^k) CNTn(pk) 1 ∼ n 1\sim n 1n中恰好被 p k p^k pk整除且无法被 p k + 1 p^{k+1} pk+1整除的数的个数。
考虑求 CNT ⁡ n ( p k ) \operatorname{CNT}_n(p^k) CNTn(pk),我们知道 ⌊ n p k ⌋ \lfloor\frac{n}{p^k}\rfloor pkn的意义是 1 ∼ n 1\sim n 1n至少可以被 p k p^k pk整除的数的个数,类比 ⌊ n p k + 1 ⌋ \lfloor\frac{n}{p^{k+1}}\rfloor pk+1n的意义就是 1 ∼ n 1\sim n 1n至少可以被 p k + 1 p^{k+1} pk+1整除的数的个数,用 ⌊ n p k ⌋ \lfloor\frac{n}{p^k}\rfloor pkn减去 ⌊ n p k + 1 ⌋ \lfloor\frac{n}{p^{k+1}}\rfloor pk+1n的话只会剩下只能 p k p^k pk整除的数的个数,即有 CNT ⁡ n ( p k ) = ⌊ n p k ⌋ − ⌊ n p k + 1 ⌋ \operatorname{CNT}_n(p^k)=\lfloor\frac{n}{p^k}\rfloor-\lfloor\frac{n}{p^{k+1}}\rfloor CNTn(pk)=pknpk+1n
但是 n n n的范围在 1 ∼ 1 0 11 1\sim10^{11} 11011,需要考虑更深一步的优化。不难发现对于每个 n < p ≤ n , p ∈ p r i m e \sqrt n<p\le n,p\in prime n <pn,pprime,其最高幂次只能为 1 1 1,对于它们的贡献,只有 2 p × CNT ⁡ n ( p ) × CNT ⁡ n ( 1 ) = 2 p ⌊ n p ⌋ ( n − ⌊ n p ⌋ ) 2p\times \operatorname{CNT}_n(p)\times \operatorname{CNT}_n(1)=2p\lfloor\frac{n}{p}\rfloor(n-\lfloor\frac{n}{p}\rfloor) 2p×CNTn(p)×CNTn(1)=2ppn(npn)。假设我们已经求得每一个整除块内的全部质数和 S ⁡ ⌊ n i ⌋ \operatorname{S}_{\lfloor\frac{n}{i}\rfloor} Sin,就可以整除分块令答案加上 S ⁡ ⌊ n i ⌋ × ⌊ n i ⌋ ( n − ⌊ n i ⌋ ) \operatorname{S}_{\lfloor\frac{n}{i}\rfloor}\times\lfloor\frac{n}{i}\rfloor(n-\lfloor\frac{n}{i}\rfloor) Sin×in(nin)。刚好min25筛中的 g ( ⌊ n i ⌋ ) g(\lfloor\frac{n}{i}\rfloor) g(in)可以求得所有在 1 ∼ ⌊ n i ⌋ 1\sim\lfloor\frac{n}{i}\rfloor 1in中的质数和,只要两两相减便可以得到每一个大于 n \sqrt n n 的整除块的全部质数和了。剩下的 n log ⁡ n \frac{\sqrt n}{\log n} lognn 个质数直接暴力按上式求即可,时间复杂度 O ⁡ ( n 0.75 log ⁡ n + n log ⁡ n × log ⁡ 2 n ) = O ⁡ ( n 0.75 log ⁡ n ) \operatorname{O}(\frac{n^{0.75}}{\log n}+\frac{\sqrt n}{\log n}\times\log^2 n)=\operatorname{O}(\frac{n^{0.75}}{\log n}) O(lognn0.75+lognn ×log2n)=O(lognn0.75)
码量不大,就是卡常卡了很久(

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=6e5+5,M=1e9+7;
const ll inv2=M/2+1;
inline ll add(ll x,ll y){return x+y>=M?x+y-M:x+y;}
inline ll sub(ll x,ll y){return x-y<0?x-y+M:x-y;}
inline ll mul(ll x,ll y){return x*y%M;}
int cnt=0,prime[N],sum[N],g[N],id1[N],id2[N],tot=0;
bitset<N> vis;
ll n,sqn,v[N];
int get(ll x){
    if(x>sqn) return id2[n/x];
    return id1[x];
}
void pre(){
    sqn=sqrt(n);
    for(int i=2;i<=sqn;++i){
        if(!vis[i]){
            sum[i]=add(sum[i-1],i);
            prime[++cnt]=i;
        }
        else sum[i]=sum[i-1];
        for(int j=1;i*prime[j]<=sqn;++j){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }
    for(ll l=1,r;l<=n;l=r+1,++tot){
        ll t=n/l;
        r=n/t;
        g[tot]=sub(mul(mul((t+1)%M,t%M),inv2),1);
        if(t>sqn) id2[r]=tot;
        else id1[t]=tot;
        v[tot]=t;
    }
    for(int i=1;i<=cnt;++i){
        ll s=1ll*prime[i]*prime[i];
        for(int j=0;s<=v[j];++j){
            int id=get(v[j]/prime[i]);
            g[j]=sub(g[j],mul(prime[i],sub(g[id],sum[prime[i-1]])));
        }
    }
}
signed main(){
    cin>>n;
    pre();
    ll res=0,prev=g[get(sqn)];
    for(int i=sqn;i;--i){
        int id=get(n/i);
        res+=mul(sub(g[id],prev),mul(i,n-i));
        prev=g[id];
    }
    for(int i=1;i<=cnt;++i){
        ll t=0;
        for(ll j=1,a=prime[i];a<=n;++j,a*=prime[i]){
            ll t1=0;
            for(ll k=0,b=1;k<=j-1;++k,b*=prime[i])
                t1+=mul(j-k,(n/b-n/b/prime[i])%M);
            t+=mul((n/a-n/a/prime[i])%M,t1%M);
        }
        res+=mul(t%M,prime[i]);
    }
    cout<<mul(res,2)<<endl;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值