Luogu P4240 毒瘤之神的考验

题目链接

https://www.luogu.org/problemnew/show/P4240

题解

容易发现
φ ( i j ) = φ ( i ) φ ( j ) gcd ⁡ ( i , j ) φ ( gcd ⁡ ( i , j ) ) \varphi(ij)=\frac{\varphi(i)\varphi(j)\gcd(i,j)}{\varphi(\gcd(i,j))} φ(ij)=φ(gcd(i,j))φ(i)φ(j)gcd(i,j)
因此可以反演得到
∑ T = 1 min ⁡ ( n , m ) ∑ i = 1 ⌊ n / k ⌋ φ ( i k ) ∑ i = 1 ⌊ m / k ⌋ φ ( i k ) ∑ d ∣ T d φ ( d ) μ ( T d ) \sum_{T=1}^{\min(n,m)} \sum_{i=1}^{\lfloor n/k\rfloor}\varphi(ik)\sum_{i=1}^{\lfloor m/k\rfloor }\varphi(ik)\sum_{d|T}\frac{d}{\varphi(d)}\mu(\frac{T}{d}) T=1min(n,m)i=1n/kφ(ik)i=1m/kφ(ik)dTφ(d)dμ(dT)
可以设
g ( a , b ) = ∑ i = 1 a φ ( i b ) g(a,b)=\sum_{i=1}^a \varphi(ib) g(a,b)=i=1aφ(ib)
这个显然可以 O ( n log ⁡ n ) O(n\log n) O(nlogn)预处理。

同理,设
f ( T ) = ∑ d ∣ T d φ ( d ) μ ( T d ) f(T)=\sum_{d|T}\frac{d}{\varphi(d)}\mu(\frac{T}{d}) f(T)=dTφ(d)dμ(dT)
这个也可以 O ( n log ⁡ n ) O(n\log n) O(nlogn)预处理。

那么答案就是
∑ T = 1 min ⁡ ( n , m ) g ( ⌊ n T ⌋ , T ) g ( ⌊ m T ⌋ , T ) f ( T ) \sum_{T=1}^{\min(n,m)}g(\lfloor\frac{n}{T}\rfloor,T)g(\lfloor\frac{m}{T}\rfloor,T)f(T) T=1min(n,m)g(Tn,T)g(Tm,T)f(T)
钦定一个 B B B,对于 i , j ≤ B i,j\leq B i,jB,设
H ( i , j , k ) = ∑ T = 1 k g ( i , T ) g ( j , T ) f ( T ) H(i,j,k)=\sum_{T=1}^k g(i,T)g(j,T)f(T) H(i,j,k)=T=1kg(i,T)g(j,T)f(T)
这个可以 O ( n B 2 ) O(nB^2) O(nB2)预处理。

考虑处理询问,对于 ⌊ n T ⌋ , ⌊ m T ⌋ ≤ B \lfloor\frac{n}{T}\rfloor,\lfloor\frac{m}{T}\rfloor\leq B Tn,TmB,整除分块,用预处理出的 H ( ⌊ n T ⌋ , ⌊ m T ⌋ , r ) − H ( ⌊ n T ⌋ , ⌊ m T ⌋ , l − 1 ) H(\lfloor\frac{n}{T}\rfloor,\lfloor\frac{m}{T}\rfloor,r)-H(\lfloor\frac{n}{T}\rfloor,\lfloor\frac{m}{T}\rfloor,l-1) H(Tn,Tm,r)H(Tn,Tm,l1)计入答案;否则, T ≤ n B T\leq \frac{n}{B} TBn,直接枚举即可。

时间复杂度 O ( n log ⁡ n + n B 2 + T ( n + n B ) O(n\log n+nB^2+T(\sqrt{n}+\frac{n}{B}) O(nlogn+nB2+T(n +Bn),观察到 B = T 1 / 3 B=T^{1/3} B=T1/3最优。

代码

#include <cstdio>
#include <vector>
#include <algorithm>

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

const int maxn=100000;
const int maxm=65;
const int mod=998244353;

int p[maxn+10],prime[maxn+10],cnt,mu[maxn+10],phi[maxn+10],inv[maxn+10],f[maxn+10];
std::vector<int> g[maxn+10],h[maxm+2][maxm+2];

int getprime()
{
  inv[0]=inv[1]=1;
  for(int i=2; i<=maxn; ++i)
    {
      inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    }
  p[1]=mu[1]=phi[1]=1;
  for(int i=2; i<=maxn; ++i)
    {
      if(!p[i])
        {
          prime[++cnt]=i;
          mu[i]=mod-1;
          phi[i]=i-1;
        }
      for(int j=1; (j<=cnt)&&(i*prime[j]<=maxn); ++j)
        {
          int x=i*prime[j];
          p[x]=1;
          if(i%prime[j]==0)
            {
              mu[x]=0;
              phi[x]=phi[i]*prime[j];
              break;
            }
          mu[x]=mod-mu[i];
          phi[x]=phi[i]*(prime[j]-1);
        }
    }
  for(int i=1; i<=maxn; ++i)
    {
      int v=1ll*i*inv[phi[i]]%mod;
      for(int j=1; j<=maxn/i; ++j)
        {
          f[i*j]=(f[i*j]+1ll*mu[j]*v)%mod;
        }
    }
  for(int i=1; i<=maxn; ++i)
    {
      g[i].push_back(0);
      for(int j=1; j<=maxn/i; ++j)
        {
          int k=((i==1)?0:g[i-1][j])+phi[i*j];
          if(k>=mod)
            {
              k-=mod;
            }
          g[i].push_back(k);
        }
    }
  for(int i=1; i<=maxm; ++i)
    {
      for(int j=1; j<=maxm; ++j)
        {
          h[i][j].push_back(0);
          for(int k=1; k<=std::min(maxn/i,maxn/j); ++k)
            {
              h[i][j].push_back((h[i][j][k-1]+1ll*g[i][k]*g[j][k]%mod*f[k])%mod);
            }
        }
    }
  return 0;
}

int T,n,m;

int main()
{
  getprime();
  T=read();
  while(T--)
    {
      n=read();
      m=read();
      int ans=0,k=std::max(n,m)/maxm;
      for(int i=1; i<=k; ++i)
        {
          ans=(ans+1ll*g[n/i][i]*g[m/i][i]%mod*f[i])%mod;
        }
      for(int l=k+1,r; l<=std::min(n,m); l=r+1)
        {
          r=std::min(n/(n/l),m/(m/l));
          ans+=h[n/l][m/l][r]-h[n/l][m/l][l-1];
          if(ans>=mod)
            {
              ans-=mod;
            }
          if(ans<0)
            {
              ans+=mod;
            }
        }
      printf("%d\n",ans);
    }
  return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值