BZOJ 3512: DZY Loves Math IV

11 篇文章 0 订阅
7 篇文章 0 订阅

Description

给定n,m,求 ni=1mj=1φ(ij) mod 1e9+7
n<=100,000,m<=1,000,000,000

Solution

以为这题是之前那道多校的原题,高兴地去刷了,然后,两天过去了。。
n 那么小显然暴力枚举即可
同样的,我们定义sum(n,m)表示 mi=1φ(in)
然而这里并不是 free square number
不要紧,显然如果有个因数 p >1的幂,则 sum(n,m)=pαs1sum(n/(pαs1))
那么一开始我们来一发质因数分解即可
然后同样的, sum(n,m)=φ(p)sum(np,m)+sum(n,mp)
然后突然发现 m TM那么大。。递归结尾返回什么。。
暴力搞肯定不行了,突然想到有种东西叫杜教筛
赶快拿起来研究了一下
就然我们来推导一下φ的前缀和 ϕ

ϕ(n)=1=1nφ(i)

d|iφ(d)=n

=>φ(n)=nd|n,d<nφ(d)

带入原式可得
ϕ(n)=i=1nid|i,d<iφ(d)

=n(n+1)2d|i,d<iφ(d)

换一下枚举顺序,枚举i/d可得
ϕ(n)=n(n+1)2i=2ni=1ni

=n(n+1)2i=2nϕ(ni)

网上有个小哥蛮逗,他杜教筛的时候暴力for后面的东西,然后吐槽说常数有点大。。
事实上大家都会按值域搞一发即可

复杂度懒得证明,直接拿来用吧如果预处理前面的项 O(n23)

接下来是精髓,卡常数的部分
照理说这样已经可以搞出来了,然而我在常数的坑里徘徊了n久
一开始我在sum的时候套了个map,然后MLE了5次(当然那时样例跑了20S+)
后来发现去掉map不仅时间变成了10S-而且不会MLE了。。
然后我在杜教筛的时候记搜中把map用手写hash_table,又快了2S
现在的时间变成了8S整
我从网上随手拔了个AC代码,亲测7.6S,交一发,A了。。
于是我就各种不爽。
我在筛素数的时候把每个数字的最大因数给筛出来,然后看似可以省掉一个 n ?然而亲测没有效果。。
为什么那个代码同样在求sum的时候套了个map,为什么不好会MLE,而且可以跑到7.6S,遥(gui)遥(ce)领(ping)先(qi)
我再三反思,一定是辗转相除的次数太多了,调用递归的次数太多了。。
完了多半是废了。。
这时候我突然发现一开始的那个带进去算sum的东西因为已经因式分解了,所以值域很窄?
无脑套了个记忆化
跑出了3S惊人的速度!!!
欣慰地笑了,感觉自己可以卡世界上一切常数了

Code

/**************************************************************
    Problem: 3512
    User: bblss123
    Language: C++
    Result: Accepted
    Time:8292 ms
    Memory:91760 kb
****************************************************************/

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int M=7e6+5;
const int P=1e9+7;
typedef long long ll;
inline void Mod_add(int &a,int b){
    if((a+=b)>=P)a-=P;
}
inline int Mod(int x){
    if(x>=P)return x-P;
    if(x<0)return x+P;
    return x;
}
int prime[M>>2],phi[M],s[M],sf[M],t=0;
inline void Eular(){
    phi[1]=1;
    for(int i=2;i<M;++i){
        if(!sf[i])prime[t++]=i,phi[i]=i-1,sf[i]=i;
        for(int j=0,p;j<t&&1ll*(p=prime[j])*i<M;++j){
            sf[i*p]=p;
            if(i%p==0){phi[i*p]=p*phi[i];break;}
            phi[i*p]=phi[i]*(p-1);
        }
    }
    for(int i=1;i<M;++i)
        s[i]=Mod(s[i-1]+phi[i]);
}
struct hash_table{
    int tot,head[10000];
    struct Edge{
        int s,c,nxt;
    }edge[100000];
    inline hash_table(){memset(head,-1,sizeof(head));}
    inline void add(int w,int &x,int &c){
        edge[tot]=(Edge){x,c,head[w]};
        head[w]=tot++;
    }
    inline int find(int &x){
        int w=x>>20;
        for(int i=head[w];~i;i=edge[i].nxt){
            int s=edge[i].s;
            if(s==x)return edge[i].c;
        }
        return -1;
    }
    inline void insert(int &x,int &val){
        add(x>>20,x,val);
    }
}mp;
inline int verphi(int x){
    if(x<M)return s[x];
    int w=mp.find(x);
    if(~w)return w;
    int k=0;
    for(int i=2,s;i<=x;++i){
        s=x/(x/i);
        Mod_add(k,(ll)(s-i+1)*verphi(x/i)%P);
        i=s;
    }
    k=(((ll)x*(x+1)>>1)-k)%P;
    mp.insert(x,k);
    return k;
}
inline int Divide(int x){
    int res=1;
    for(int i=0;prime[i]*prime[i]<=x;++i)
        if(x%prime[i]==0){
            int c=0;
            for(;x%prime[i]==0;++c)x/=prime[i];
            for(--c;c--;)res*=prime[i];
        }
    return res;
}
inline int sum(int n,int m){
    if(!m)return 0;
    if(m==1)return phi[n];
    if(n==1)return verphi(m);
    int &k=sf[n];
    return ((ll)sum(n/k,m)*(k-1)+sum(n,m/k))%P;
}
int f[100005];
int main(){
    Eular();
    int n,m,ans=0;
    scanf("%d %d",&n,&m);
    memset(f,-1,sizeof(f));
    for(int i=1;i<=n;++i){
        ll k=Divide(i),t=i/k;
        if(~f[t])Mod_add(ans,k*f[t]%P);
        else Mod_add(ans,k*(f[t]=sum(t,m))%P);
    }
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值