bzoj3994: [SDOI2015]约数个数和

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=3994

这道题目学到的

  数学是硬伤….OI早晚要受到牵连,这不,叫这道题卡了一晚上。
  有些方法经常要用到
  一、枚举次序的变化,如果要先枚举一个数字 x ,再枚举其倍数d,可以把 d 拿到前面来,先枚举d再枚举其因子 x ,这样的好处是x的上界变成了 d ,而不是和询问有关的n
  二、 ni=1Mj=1A[gcd(i,j)=1] 其中A是随便一个式子,就等价于 ni=1Mj=1Ad|gcd(i,j)μ(d)
  三、 nid=ndi
  四、 ijf(i)g(j)=if(i)jg(j) ,就是说 后面和枚举变量没关的东西统统可以拿出来。

题解

  一个结论,

i=1Nj=1Md(ij)=i1Nj=1MNiMj[gcd(i,j)=1]

  应用第二条
  
=i=1Nj=1MNiMjd|gcd(i,j)μ(d)

  应用第一、四条
  
=d=1min(N,M)μ(d)d|iNNid|jMMj

  继续
  
=d=1min(N,M)μ(d)i=1NdNidj=1MdMjd

  应用第三条
  
=d=1min(N,M)μ(d)i=1NdNdij=1MdMdj

  可以发现,后面两个 的值都只和上界有关,因此可以预处理 f[x]=xi=1xi
  最终的答案为
  
d=1min(n,m)μ(d)f(Nd)f(Md)

  这个可以 N
   f(x) 的预处理,我最开始是用 NN 处理的,后来听说这个东西就是约数个数和,于是改用了 NlogN 的方法。
  代码在bzoj是AC的,但是洛谷上会TLE(我怎么知道是什么情况??)。

代码

//莫比乌斯反演 
#include <cstdio>
#include <algorithm>
#define ll long long
#define maxn 50000
using namespace std;
ll mu[maxn+10], prime[maxn+10], f[maxn+10], mark[maxn+10], s[maxn+10];
void init()
{
    ll i, j;
    s[1]=mu[1]=1;
    for(i=2;i<=maxn;i++)
    {
        if(!mark[i])prime[++prime[0]]=i,mu[i]=-1;
        for(j=1;j<=prime[0] and i*prime[j]<=maxn;j++)
        {
            mark[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
        s[i]=s[i-1]+mu[i];
    }
    for(i=1;i<=maxn;i++)
        for(j=i;j<=maxn;j+=i)f[j]++;
    for(i=1;i<=maxn;i++)f[i]+=f[i-1];
}
ll calc(ll n, ll m)
{
    ll ans=0, i, last;
    if(n>m)swap(n,m);
    for(i=1;i<=n;i=last+1)
    {
        last=min(n/(n/i),m/(m/i));
        ans+=(s[last]-s[i-1])*f[n/i]*f[m/i];
    }
    return ans;
}
int main()
{
    ll T, N, M;
    init();
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld%lld",&N,&M);
        printf("%lld\n",calc(N,M));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值