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;
}
发布了950 篇原创文章 · 获赞 77 · 访问量 21万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 撸撸猫 设计师: 设计师小姐姐

分享到微信朋友圈

×

扫一扫,手机浏览