zoukankan      html  css  js  c++  java
  • 【洛谷P4240】毒瘤之神的考验

    题目

    题目链接:https://www.luogu.com.cn/problem/P4240
    (Q) 次询问,每次询问给出 (n,m),求

    [left(sum^{n}_{i=1}sum^{m}_{j=1}varphi(ij) ight)mod 998244353 ]

    (n,mleq 10^5)(Qleq 10^4)

    思路

    [varphi(i)varphi(j)=iprod_{p|i}frac{p-1}{p} imes jprod_{p|j}frac{p-1}{p} ]

    [varphi(i)varphi(j)=ij imes prod_{p|ij}frac{p-1}{p} imesprod_{p|gcd(i,j)}frac{p-1}{p} ]

    [varphi(i)varphi(j)gcd(i,j)=ij imes prod_{p|ij}frac{p-1}{p} imes gcd(i,j)prod_{p|gcd(i,j)}frac{p-1}{p} ]

    [varphi(ij)=frac{varphi(i)varphi(j)gcd(i,j)}{varphi(gcd(i,j))} ]

    所以

    [sum^{n}_{i=1}sum^{m}_{j=1}varphi(ij)=sum^{n}_{i=1}sum^{n}_{j=1}frac{varphi(i)varphi(j)gcd(i,j)}{varphi(gcd(i,j))} ]

    [=sum^{n}_{d=1}frac{d}{varphi(d)}sum^{n}_{i=1}sum^{n}_{j=1}varphi(i)varphi(j) imes[gcd(i,j)=d] ]

    [=sum^{n}_{d=1}frac{d}{varphi(d)}sum^{lfloorfrac{n}{d} floor}_{k=1}mu(k)sum^{lfloorfrac{n}{dk} floor}_{i=1}sum^{lfloorfrac{m}{dk} floor}_{j=1}varphi(idk)varphi(jdk) ]

    [=sum^{n}_{k=1}left(sum_{d|k}frac{dmu(frac{k}{d})}{varphi(d)} imes sum^{lfloorfrac{n}{k} floor}_{i=1}varphi(ik) imes sum^{lfloorfrac{m}{k} floor}_{j=1}varphi(jk) ight) ]

    (f(i)=sum_{d|i}frac{dmu(frac{i}{d})}{varphi(d)})(g(k,i)=sum^{i}_{j=1}varphi(jk))。前者枚举倍数可以 (O(nlog n)) 预处理出来,后者由于 (k imes ileq n),也可以在 (O(nlog n)) 内预处理。
    然后原式

    [=sum^{n}_{k=1}f(k) imes g(k,lfloorfrac{n}{k} floor) imes g(k,lfloorfrac{m}{k} floor) ]

    整除分块,令 (h(a,b,k)=sum^{n}_{k=1}f(k) imes g(k,a) imes g(k,b))。当然这个不可能全部预处理出来,考虑平衡规划。
    设定一个阈值 (B)。预处理出 (max(a,b)leq B) 时所有的 (h),当 (max(a,b)>B) 时,说明 (kleq lfloorfrac{n}{B} floor)。直接暴力求即可。
    时间复杂度 (O(nlog n+nB^2+Q(sqrt{n}+frac{n}{B})))。空间复杂度 (O(nB^2))。取 (B=20) 即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=100010,B=20,MOD=998244353;
    int Q,n,m,mu[N],phi[N],prm[N];
    bool v[N];
    ll ans,f[N],h[B+2][B+2][N];
    vector<ll> g[N];
    
    void findprm(int n)
    {
    	mu[1]=phi[1]=1;
    	for (int i=2;i<=n;i++)
    	{
    		if (!v[i]) prm[++m]=i,phi[i]=i-1,mu[i]=-1;
    		for (int j=1;j<=m;j++)
    		{
    			if (i>n/prm[j]) break;
    			v[i*prm[j]]=1;
    			mu[i*prm[j]]=-mu[i]; phi[i*prm[j]]=phi[i]*(prm[j]-1);
    			if (!(i%prm[j]))
    			{
    				mu[i*prm[j]]=0; phi[i*prm[j]]=phi[i]*prm[j];
    				break;
    			}
    		}
    	}
    }
    
    ll fpow(ll x,ll k)
    {
    	ll ans=1;
    	for (;k;k>>=1,x=x*x%MOD)
    		if (k&1) ans=ans*x%MOD;
    	return ans;
    }
    
    int main()
    {
    	findprm(N-1);
    	for (int i=1;i<N;i++)
    	{
    		ll inv=fpow(phi[i],MOD-2);		
    		for (int j=i;j<N;j+=i)
    			f[j]=(f[j]+1LL*i*mu[j/i]*inv)%MOD;
    		g[i].push_back(0LL);
    		for (int j=1;i*j<N;j++)
    			g[i].push_back((g[i][j-1]+phi[i*j])%MOD);
    	}
    	for (int i=1;i<=B;i++)
    		for (int j=1;j<=B;j++)
    			for (int k=1;k<N;k++)
    				if (i*k<N && j*k<N)
    					h[i][j][k]=(h[i][j][k-1]+f[k]*g[k][i]%MOD*g[k][j])%MOD;
    	scanf("%d",&Q);
    	while (Q--)
    	{
    		scanf("%d%d",&n,&m);
    		if (n>m) swap(n,m);
    		ans=0;
    		for (int l=1,r;l<=n;l=r+1)
    		{
    			r=min(n/(n/l),m/(m/l));
    			if (m/l<=B) ans=(ans+h[n/l][m/l][r]-h[n/l][m/l][l-1])%MOD;
    			else
    			{
    				for (int i=l;i<=r;i++)
    					ans=(ans+f[i]*g[i][n/i]%MOD*g[i][m/i])%MOD;
    			}
    		}
    		cout<<(ans+MOD)%MOD<<"
    ";
    	}
    	return 0;
    }
    
  • 相关阅读:
    SQL Server将一列的多行内容拼接成一行的问题讨论
    SQL 获取 IDENTITY 三种方法 SCOPE_IDENTITY、IDENT_CURRENT 和 @@IDENTITY的区别
    构建高性能服务(二)
    乐观锁解决高并发
    c#问答篇:对象与引用变量-----初学者的困惑
    vs调试 本地IIS
    【转】android adb常用指令
    【转】使用 JMeter 完成常用的压力测试
    【转】利用 Apache JMeter 测试 WebSphere 性能
    【转】用JMeter来测试Tomcat的性能
  • 原文地址:https://www.cnblogs.com/stoorz/p/15375271.html
Copyright © 2011-2022 走看看