PE 372 【类欧几里德】【Stern-Brocot Tree】

题目大意:求 y2x2 为奇数的个数,其中 2106<x,y<=109

k=y2x2 ,有 x2k<=y2<x2(k+1)
xk<=y<xk+1
k为奇数,所以是计算在 kx<=y 区域内的整点数,奇数加,偶数减

对于直线 y=kx ,k 为无理数,可以找到一个大于等于 k 的最小分数,答案不变,这步用 Stern-Brocot Tree 就可以实现

现在就是求 y>=axb
考虑用类欧几里德

Ni=0ax+bc=acN(N+1)2+bc(N+1)+Ni=0(a%c)x+b%cc

现在就有 a<c,b<c

M=aN+bc
原式 = Nx=0My=0[y<ax+bc]

现在在简化这个式子
c(y+1)<=ax+b
cy+cb<=ax
cy+cb1<ax
x>cy+cb1a

现在就变成了求
My=0Nx=0[x>cy+cb1a]
= M1y=0(Ncy+cb1a)
= NMM1y=0cy+cb1a)
//这里要求的是 x<=N ,但 y 取到 M 的时候会超出范围,而考虑到 y = M 时对答案无贡献,所以只用加到 M - 1

现在就可以在 O(log) 的复杂度内计算出答案了

友情提示:这道题要用 long double

【答案】301450082318807027

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#define D long double
#define LL long long
using namespace std;

void find(LL la,LL lb,LL ra,LL rb,LL &p,LL &q,LL lim,D aim)
{
    LL a = la + ra,b = lb + rb;
    if (a > lim || b > lim) return;
    if ((D)a / b < aim)
        find(a,b,ra,rb,p,q,lim,aim);
    else
        p = a,q = b,find(la,lb,a,b,p,q,lim,aim);
}

LL F(LL a,LL b,LL c,LL N)
{
    LL ret = N * (N + 1) / 2 * (a / c) + (N + 1) * (b / c);
    a %= c,b %= c;
    LL M = (a * N + b) / c;
    if (!M) return ret;
    LL t = a ? N * M - F(c,c - b - 1,a,M - 1) : 0;
    return ret + t;
}

LL cal(LL N,LL k)
{
    LL t = sqrt(k),a = 0,b = 0,c = 1;
    if (t * t != k) find(0,1,1,0,c,a,N,sqrt((D)k));
        else a = 1,c = t;
    return F(a,b,c,N);
}

int main()
{
    LL N = 1e9,M = 2*1e6,lim = N * N / (M + 1) / (M + 1),ans = 0;
    for (LL k = 1;k <= lim;k ++)
    {
        LL l = (LL)M * (sqrt(k)),t = cal(N,k) - cal(l,k) - (N - l) * M;
        ans += (k & 1) ? t : -t;
    }
    cout << ans << endl;

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值