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\)的倍数进行打表
得到规律:
对于任意大于\(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\)
或者这里也可以打表找规律
对于\(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;
}