Codeforces 1485C - Floor and Mod (分块、数学)

Codeforces Round #701 (Div. 2) C. Floor and Mod


题意

给定\(x,y\),询问当\(1\le a\le x,\ 1\le b\le y\)时,\(\lfloor \frac a b \rfloor=a\ mod\ b\)的对数

限制

\(1\le x,y\le 10^9\)




思路

(想法有很多种,本文主要通过分块求解)

(并没有那么难,但为什么总是往难的方向想呢……)

读懂题意后如果用公式化简

根据\(a\ mod\ b=a-\lfloor \frac a b \rfloor*b\),最多只能够化到\(a=(b+1)*\lfloor \frac a b \rfloor\)

可得\(\frac a {b+1}=\lfloor \frac a b \rfloor\)

明显,\(b+1\)一定是\(a\)的因子

(然后就想不到其他规律了)


那么我们枚举\(b\),再枚举\(b+1\)的倍数进行打表

pic1

得到规律:

对于任意大于\(2\)的正整数\(b\)

满足题意的\(a\)最多有\(b-1\)项,每一项都是\(b+1\)的倍数,最大项为\((b-1)*(b+1)\)


所以对于每个\(b\),我们可以先求出\(1\)\(x\)内有多少\(b+1\)的倍数(即\(\lfloor\frac x {b+1}\rfloor\)个)

在计算答案时,根据规律需要将其与\(b-1\)取小

但是这种方法的时间复杂度为\(O(y=10^9)\),不可行


考虑优化,发现如果\((b-1)*(b+1)\gt x\),即\(\lfloor\frac x {b+1}\rfloor \lt b-1\)

\(x\)的限制,会有一段段连续的\(b\)对应的答案数量相同,且答案均为\(\lfloor\frac x {b+1}\rfloor\)

直接考虑分块,令\(t=\lfloor\frac x {b+1}\rfloor\)表示当前段的答案,再通过\(\lfloor\frac x t\rfloor\)来求出满足答案为\(t\)的最大的\(b+1\)的值

故此时最大的\(b\)应为\(\lfloor\frac x t\rfloor-1\),注意与\(y\)取小即可

该段的答案即段长度*t,其后让\(b\)直接成为右端点\(+1\)即可


\((b-1)*(b+1)\le x\)时,对于每个\(b\)的答案数量均为\(b-1\)

故我们可以求出满足\((b-1)*(b+1)\le x\)以及\(b\le y\)的最大的\(b\)

那么答案就是\(1+2+3+\cdots +(b-1)\),直接等差求和公式即可


对于这个最大的满足\((b-1)*(b+1)\le x\)\(b\),先忽略条件\(b\le y\),结论是\(maxb=\lfloor\sqrt x\rfloor\)\(maxb=\lfloor\sqrt x\rfloor +1\)

或者这里也可以打表找规律

pic2

对于\(maxb+1\)\(y\),还是分块处理


代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

void solve()
{
    int x,y;
    scanf("%d%d",&x,&y);
    
    int b=sqrt(x)+1;
    if(1LL*(b-1)*(b+1)>x)
        b--;
    b=min(b,y); //注意与y取小
    ll ans=1LL*b*(b-1)/2;
    
    for(b++;b<=y;)
    {
        int t=x/(b+1);
        if(t==0)
            break;
        
        int nxt=min(x/t-1,y); // 注意x/t是对于b+1的最大值
        ans+=1LL*(nxt-b+1)*t;
        
        b=nxt+1;
    }
    printf("%lld\n",ans);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
        solve();
    return 0;
}


posted @ 2021-02-13 01:16  StelaYuri  阅读(142)  评论(0编辑  收藏  举报