Codeforces Round #701 (Div. 2) C. Floor and Mod
给定 x , y x,y x,y,询问当 1 ≤ a ≤ x , 1 ≤ b ≤ y 1\le a\le x,\ 1\le b\le y 1≤a≤x, 1≤b≤y时, ⌊ a b ⌋ = a m o d b \lfloor \frac a b \rfloor=a\ mod\ b ⌊ba⌋=a mod b的对数
1 ≤ x , y ≤ 1 0 9 1\le x,y\le 10^9 1≤x,y≤109
(想法有很多种,本文主要通过分块求解)
(并没有那么难,但为什么总是往难的方向想呢……)
读懂题意后如果用公式化简
根据 a m o d b = a − ⌊ a b ⌋ ∗ b a\ mod\ b=a-\lfloor \frac a b \rfloor*b a mod b=a−⌊ba⌋∗b,最多只能够化到 a = ( b + 1 ) ∗ ⌊ a b ⌋ a=(b+1)*\lfloor \frac a b \rfloor a=(b+1)∗⌊ba⌋
可得 a b + 1 = ⌊ a b ⌋ \frac a {b+1}=\lfloor \frac a b \rfloor b+1a=⌊ba⌋
明显, b + 1 b+1 b+1一定是 a a a的因子
(然后就想不到其他规律了)
那么我们枚举 b b b,再枚举 b + 1 b+1 b+1的倍数进行打表
得到规律:
对于任意大于 2 2 2的正整数 b b b
满足题意的 a a a最多有 b − 1 b-1 b−1项,每一项都是 b + 1 b+1 b+1的倍数,最大项为 ( b − 1 ) ∗ ( b + 1 ) (b-1)*(b+1) (b−1)∗(b+1)
所以对于每个 b b b,我们可以先求出 1 1 1到 x x x内有多少 b + 1 b+1 b+1的倍数(即 ⌊ x b + 1 ⌋ \lfloor\frac x {b+1}\rfloor ⌊b+1x⌋个)
在计算答案时,根据规律需要将其与 b − 1 b-1 b−1取小
但是这种方法的时间复杂度为 O ( y = 1 0 9 ) O(y=10^9) O(y=109),不可行
考虑优化,发现如果 ( b − 1 ) ∗ ( b + 1 ) > x (b-1)*(b+1)\gt x (b−1)∗(b+1)>x,即 ⌊ x b + 1 ⌋ < b − 1 \lfloor\frac x {b+1}\rfloor \lt b-1 ⌊b+1x⌋<b−1时
受 x x x的限制,会有一段段连续的 b b b对应的答案数量相同,且答案均为 ⌊ x b + 1 ⌋ \lfloor\frac x {b+1}\rfloor ⌊b+1x⌋
直接考虑分块,令 t = ⌊ x b + 1 ⌋ t=\lfloor\frac x {b+1}\rfloor t=⌊b+1x⌋表示当前段的答案,再通过 ⌊ x t ⌋ \lfloor\frac x t\rfloor ⌊tx⌋来求出满足答案为 t t t的最大的 b + 1 b+1 b+1的值
故此时最大的 b b b应为 ⌊ x t ⌋ − 1 \lfloor\frac x t\rfloor-1 ⌊tx⌋−1,注意与 y y y取小即可
该段的答案即段长度*t,其后让 b b b直接成为右端点 + 1 +1 +1即可
当 ( b − 1 ) ∗ ( b + 1 ) ≤ x (b-1)*(b+1)\le x (b−1)∗(b+1)≤x时,对于每个 b b b的答案数量均为 b − 1 b-1 b−1
故我们可以求出满足 ( b − 1 ) ∗ ( b + 1 ) ≤ x (b-1)*(b+1)\le x (b−1)∗(b+1)≤x以及 b ≤ y b\le y b≤y的最大的 b b b
那么答案就是 1 + 2 + 3 + ⋯ + ( b − 1 ) 1+2+3+\cdots +(b-1) 1+2+3+⋯+(b−1),直接等差求和公式即可
对于这个最大的满足 ( b − 1 ) ∗ ( b + 1 ) ≤ x (b-1)*(b+1)\le x (b−1)∗(b+1)≤x的 b b b,先忽略条件 b ≤ y b\le y b≤y,结论是 m a x b = ⌊ x ⌋ maxb=\lfloor\sqrt x\rfloor maxb=⌊x⌋或 m a x b = ⌊ x ⌋ + 1 maxb=\lfloor\sqrt x\rfloor +1 maxb=⌊x⌋+1
或者这里也可以打表找规律
对于 m a x b + 1 maxb+1 maxb+1到 y y y,还是分块处理
#include
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;
}
To cnblogs