bzoj2671: Calc

链接

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

题解

  天啊噜!这道题好生麻烦
  我尽量长话短说。
  观察一下条件:

(a+b)|ab ( a + b ) | a b

  我们数论的算法都是和 gcd g c d 有关的,因此肯定要转到 gcd g c d 上来,令 d=gcd(a,b) d = g c d ( a , b ) a=xd,b=yd a = x d , b = y d ,写成分数
  
xyd2d(x+y) x y d 2 d ( x + y )

  即
xydx+y x y d x + y

  因为 gcd(x,y)=1 g c d ( x , y ) = 1 ,所以 gcd(x,x+y)=1 g c d ( x , x + y ) = 1 (很显然啊因为提不出公因式),同理 gcd(y,x+y)=1 g c d ( y , x + y ) = 1 ,因此 gcd(xy,x+y)=1 g c d ( x y , x + y ) = 1 ,所以想要让上式为整数,必有 (x+y)|d ( x + y ) | d 。而这样的 d d 有多少个?先不要匆忙下结论说有Nx+y个,因为 d d xy是有关联的。
  根据条件可以列出以下式子
  
xdNydNx+yd { x d ≤ N y d ≤ N x + y ≤ d

  将第三个式子左右同乘以 y y ,得y(y+x)N
  而 x x 的最小值是1,因此y(y+1)N。可以发现, y y N级别的,因此可以枚举。而 dNy d ≤ ⌊ N y ⌋ ,所以d是有上界的。
  那么写出答案
  
ans=x=1Ny=x+1NNyx+y[gcd(x,y)=1] a n s = ∑ x = 1 N ∑ y = x + 1 N ⌊ ⌊ N y ⌋ x + y ⌋ [ g c d ( x , y ) = 1 ]

  这样的复杂度是稳定的 O(NlogN) O ( N l o g N ) 。那个 log l o g 是求 gcd g c d 时候的复杂度。实际测试:TLE。
  下面进入丧心病狂的部分,首先进行莫比乌斯反演:
  
x=1Ny=x+1NNyx+yd|gcd(x,y)μ(d) ∑ x = 1 N ∑ y = x + 1 N ⌊ ⌊ N y ⌋ x + y ⌋ ∑ d | g c d ( x , y ) μ ( d )

  
d=1Nμ(d)x=1Ndy=x+1NdNyd(xd+yd) ∑ d = 1 N μ ( d ) ∑ x = 1 ⌊ N d ⌋ ∑ y = x + 1 ⌊ N d ⌋ ⌊ ⌊ N y d ⌋ ( x d + y d ) ⌋

  注意这里的 x x y已经不是原来的 x x y了,现在的 xd x d yd y d 分别对应以前的 x x y
  
d=1Nμ(d)x=1Ndy=x+1NdNyd2x+y ∑ d = 1 N μ ( d ) ∑ x = 1 ⌊ N d ⌋ ∑ y = x + 1 ⌊ N d ⌋ ⌊ ⌊ N y d 2 ⌋ x + y ⌋

  这样比原来稍稍快一点,复杂度还是 O(N1.5) O ( N 1.5 ) 实际测试T掉了但是速度的确比原来快了接近10倍(有时候光看复杂度也不可靠啊),跑最大的数据在我的笔记本上只用5s,看来再加一个小优化就差不多能过了呢。
  丧病的优化,让我想了一整节晚自习,令 s=x+y s = x + y
  
d=1Nμ(d)y=2Nds=1+y2y1Nyd2s ∑ d = 1 N μ ( d ) ∑ y = 2 ⌊ N d ⌋ ∑ s = 1 + y 2 y − 1 ⌊ ⌊ N y d 2 ⌋ s ⌋

  分块,复杂度变为 O(N1.25) O ( N 1.25 ) ,别看这个复杂度,常数可是极小的,真实的复杂度应该比这个小的多,不过我懒得算了….bzoj上1628s过,最大数据在本机只跑0.4511s

代码

//莫比乌斯反演 
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 50000
#define ll long long
using namespace std;
ll prime[maxn+10], mu[maxn+10], N, mark[maxn+10];
void init()
{
    ll i, j;
    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];
        }
    }
}
void work()
{
    ll ans=0, d, t, sqrtn=sqrt(N), tmp, y, last, s;
    for(d=1;d*(d+1)<=N;d++)
    {
        tmp=0;
        for(y=2;y<=sqrtn/d;y++)
        {
            t=N/(d*d*y);
            for(s=1+y;s<=y+y-1 and s<=t;s=last+1)
            {
                last=t/(t/s);
                tmp+=(min(last,y+y-1)-s+1)*(t/s);
            }
        }
        ans+=tmp*mu[d];
    }
    printf("%lld",ans);
}
int main()
{
    init();
    scanf("%lld",&N);
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值