【题解】Luogu-P4240 毒瘤之神的考验

Page Views Count

可以得到:

φ(ij)=φ(i)φ(j)φ(gcd(i,j))gcd(i,j)=φ(lcm(i,j))gcd(i,j)

证明考虑 φ 的展开式。

选取中间的式子带进去化简。

于是无脑反演可以得到:

T=1nkTμ(Tk)kφ(k)i=1n/Tφ(iT)j=1m/Tφ(jT)

里面有关于 T 的部分,不能直接预处理某个函数接上数论分块。

f(n)=knμ(nk)kφ(k) 暴力卷积 O(nlogn) 可以预处理。

再设 g(n,k)=i=1nφ(ik),由于 nk105,所以合法状态数是 O(nlogn) 的,可以 vector 开空间预处理,递推式 g(n,k)=g(n1,k)+φ(nk)

但是这样必须逐个枚举 T,但我们希望数论分块。

之后比较优美之处在于,上界 nT 和枚举的 T 看起来可以平衡,也就是当 T 较大时,nT 较小,可以暴力预处理,而 T 较小时暴力逐个算就完了。

这时候我们不再关心 n,m 了,主要是把所有 (nT,mT) 的值都列出来,此时还需要知道 T 的值,于是设 h(n,a,b)=i=1nf(i)×g(a,i)×g(b,i),那么有递推式 h(n,a,b)=h(n1,a,b)+f(n)×g(a,n)×g(b,n)

这样就只要设定一个阈值 B,当 nTB 时使用 h 计算,具体是形如 h(r,nl,ml)h(l1,nl,ml),其他枚举 Tf,g 计算。预处理 h(n,a,b) 时钦定 ba,这样状态数是 O(nB)

复杂度分析大致是分暴力预处理 f,g,h,两种计算答案几部分加和,于是有 O(nlogn+nB+Tn+TnB)

点击查看代码
int t;
int n,m,B=40;
int pr[maxn],mu[maxn],phi[maxn];
bool vis[maxn];
int inv[maxn],f[maxn];
vector<int> g[maxn];
vector<int> h[41][41];
inline void linear_sieve(){
    mu[1]=1,phi[1]=1;
    for(int i=2;i<=lim;++i){
        if(!vis[i]) pr[++pr[0]]=i,mu[i]=-1,phi[i]=i-1;
        for(int j=1;j<=pr[0]&&i*pr[j]<=lim;++j){
            vis[i*pr[j]]=1;
            mu[i*pr[j]]=-mu[i],phi[i*pr[j]]=phi[i]*phi[pr[j]];
            if(i%pr[j]==0){
                mu[i*pr[j]]=0,phi[i*pr[j]]=phi[i]*pr[j];
                break;
            }
        }
    }
    inv[1]=1;
    for(int i=2;i<=lim;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=lim;++i){
        for(int j=1;i*j<=lim;++j){
            int now=(1ll*mu[i]*j*inv[phi[j]]%mod+mod)%mod;
            f[i*j]=(f[i*j]+now)%mod;
        }
    }
    for(int i=0;i<=lim;++i) g[0].push_back(0); 
    for(int i=1;i<=lim;++i){
        g[i].push_back(0);
        for(int j=1;i*j<=lim;++j){
            g[i].push_back(0);
            g[i][j]=(g[i-1][j]+phi[i*j])%mod;
        }
    }
    for(int j=1;j<=B;++j){
        for(int k=1;k<=B;++k){
            h[j][k].push_back(0);
            for(int i=1;i<=lim&&i*j<=lim&&i*k<=lim;++i){
                h[j][k].push_back(0);
                h[j][k][i]=(h[j][k][i-1]+1ll*f[i]*g[j][i]%mod*g[k][i]%mod)%mod;
            }
        }
    }
}

int ans;

int main(){
    linear_sieve();
    t=read();
    while(t--){
        n=read(),m=read();
        if(n>m) swap(n,m);
        ans=0;
        for(int i=1;i<=min(m/B,n);++i){
            ans=(ans+1ll*f[i]*g[n/i][i]%mod*g[m/i][i]%mod)%mod;
        }
        for(int l=m/B+1,r;l<=n;l=r+1){
            r=min(n/(n/l),m/(m/l));
            ans=(ans+(h[n/l][m/l][r]-h[n/l][m/l][l-1]+mod)%mod)%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}

作者:SoyTony

出处:https://www.cnblogs.com/SoyTony/p/Solution_on_Luogu-P4240.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   SoyTony  阅读(50)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示