【NOIP2016提高A组模拟9.4】快速荷叶叶变换

题目

Description
这里写图片描述
Input

一行,包含两个整数N,M。

Output

1个整数,FHT(N,M) mod 1000000007的值。

Sample Input

3 4

Sample Output

1

Data Constraint
40%:n,m<=1000
60%:n,m<= 106
100%:n,m<= 109

比赛时の想法

一开始看到这道题,就想到了60分的做法,然后就开始想log级别或者是根号级别的做法,过了几分钟,突然想到了一个根号级别的做法:
我们设q=n mod i,p=n div i; 那么我们可以分情况进行讨论:
1: p<=n ,这样的情况最多有 n 个,我们直接暴力求解
2:对于剩下的还没有处理的数,他们的p值都是<= n 的,对于每一组p值相等的集合A,集合内所有的数是有等差数列的性质的,那么我们就可以先用二分查找到p值相等的区间(l,r),然后直接用等差数列的算法O(1)计算出这一段数对于答案的贡献,这样我们就可以在 n 的时间计算出答案了

正解

贴代码

const md=1000000007;
var
    n,m,i,j,k:longint;
    x,y,ans,tot,l,r,mid:int64;
procedure init;
begin
    readln(n,m);
    k:=trunc(sqrt(n));
    for i:=1 to n do
    begin
        if n div i<=k then break;
        ans:=ans+(n mod i);
    end;
    ans:=ans mod md;
end;
begin
    init;
    if (n=1) or (m=1) then
    begin
        writeln(0);
        halt;
    end;
    for i:=k downto 1 do
    begin
        l:=1;
        r:=n;
        while l<r do
        begin
            mid:=(l+r) div 2;
            if n div mid>=i then l:=mid+1 else r:=mid;
        end;
        x:=1;
        y:=l;
        while x<y do
        begin
            mid:=(x+y) div 2;
            if n div mid>i then x:=mid+1 else y:=mid;
        end;
        l:=x;
        while n div l<=i do dec(l);
        inc(l);
        while n div r<i do dec(r);
        if n mod l=0 then inc(l);
        x:=n mod l;
        if n mod r=0 then dec(r);
        y:=n mod r;
        mid:=r-l+1;
        if l>r then continue;
        ans:=(ans+((x+y)*mid) div 2) mod md;
    end;
    k:=trunc(sqrt(m));
    for i:=1 to m do
    begin
        if m div i<=k then break;
        tot:=tot+(m mod i);
    end;
    tot:=tot mod md;
    n:=m;
    for i:=k downto 1 do
    begin
        l:=1;
        r:=m;
        while l<r do
        begin
            mid:=(l+r) div 2;
            if m div mid>=i then l:=mid+1 else r:=mid;
        end;
        x:=1;
        y:=l;
        while x<y do
        begin
            mid:=(x+y) div 2;
            if m div mid>i then x:=mid+1 else y:=mid;
        end;
        l:=x;
        while m div l<=i do dec(l);
        inc(l);
        while m div r<i do dec(r);
        if n mod l=0 then inc(l);
        x:=m mod l;
        if m mod r=0 then dec(r);
        y:=m mod r;
        mid:=r-l+1;
         if l>r then continue;
        tot:=(tot+((x+y)*mid) div 2) mod md;
    end;
    ans:=ans*tot;
    writeln(ans mod md);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值