Skip to content

Commit 6b42ead

Browse files
committed
Relax the constraints of floor_sum
atcoder/ac-library#92
1 parent 4a01b55 commit 6b42ead

File tree

4 files changed

+54
-24
lines changed

4 files changed

+54
-24
lines changed

Source/AtCoderLibrary/Math/Internal/InternalMath.cs

+22
Original file line numberDiff line numberDiff line change
@@ -152,5 +152,27 @@ public static bool IsPrime(int n)
152152
}
153153
return true;
154154
}
155+
156+
public static ulong FloorSumUnsigned(ulong n, ulong m, ulong a, ulong b)
157+
{
158+
ulong ans = 0;
159+
while (true)
160+
{
161+
if (a >= m)
162+
{
163+
ans += (n - 1) * n / 2 * (a / m);
164+
a %= m;
165+
}
166+
if (b >= m)
167+
{
168+
ans += n * (b / m);
169+
b %= m;
170+
}
171+
172+
ulong yMax = a * n + b;
173+
if (yMax < m) return ans;
174+
(n, m, a, b) = (yMax / m, a, m, yMax % m);
175+
}
176+
}
155177
}
156178
}

Source/AtCoderLibrary/Math/MathLib.cs

+25-19
Original file line numberDiff line numberDiff line change
@@ -572,33 +572,39 @@ public static (long y, long m) CRT(long[] r, long[] m)
572572

573573
#region FloorSum
574574
/// <summary>
575-
/// sum_{i=0}^{<paramref name="n"/>-1} floor(<paramref name="a"/>*i+<paramref name="b"/>/<paramref name="m"/>) を返します。
575+
/// sum_{i=0}^{<paramref name="n"/>-1} floor(<paramref name="a"/>*i+<paramref name="b"/>/<paramref name="m"/>) を返します。答えがオーバーフローしたならば  mod2^64 で等しい値を返します。
576576
/// </summary>
577577
/// <remarks>
578-
/// <para>制約: 0≤<paramref name="n"/>, <paramref name="m"/>≤10^9, 0≤<paramref name="a"/>, <paramref name="b"/>&lt;<paramref name="m"/></para>
579-
/// <para>計算量: O(log(n+m+a+b))</para>
578+
/// <para>制約:</para>
579+
/// <para> 0≤<paramref name="n"/>&lt;2^32</para>
580+
/// <para> 1≤<paramref name="m"/>&lt;2^32</para>
581+
/// <para>計算量: O(log(m))</para>
580582
/// </remarks>
581583
/// <returns></returns>
582584
public static long FloorSum(long n, long m, long a, long b)
583585
{
584-
long ans = 0;
585-
while (true)
586+
Contract.Assert(0 <= n && n < (1L << 32));
587+
Contract.Assert(1 <= m && m < (1L << 32));
588+
var nn = (ulong)n;
589+
var mm = (ulong)m;
590+
ulong aa, bb;
591+
ulong ans = 0;
592+
if (a < 0)
586593
{
587-
if (a >= m)
588-
{
589-
ans += (n - 1) * n / 2 * (a / m);
590-
a %= m;
591-
}
592-
if (b >= m)
593-
{
594-
ans += n * (b / m);
595-
b %= m;
596-
}
597-
598-
long yMax = a * n + b;
599-
if (yMax < m) return ans;
600-
(n, m, a, b) = (yMax / m, a, m, yMax % m);
594+
var a2 = (ulong)InternalMath.SafeMod(a, m);
595+
ans -= nn * (nn - 1) / 2 * ((a2 - (ulong)a) / mm);
596+
aa = a2;
597+
}
598+
else aa = (ulong)a;
599+
if (b < 0)
600+
{
601+
var b2 = (ulong)InternalMath.SafeMod(b, m);
602+
ans -= nn * ((b2 - (ulong)b) / mm);
603+
bb = b2;
601604
}
605+
else bb = (ulong)b;
606+
607+
return (long)(ans + InternalMath.FloorSumUnsigned(nn, mm, aa, bb));
602608
}
603609
#endregion FloorSum
604610
}

Test/AtCoderLibrary.Test/Math/FloorSumTest.cs

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ public void FloorSum()
1515
{
1616
for (int m = 1; m < 20; m++)
1717
{
18-
for (int a = 0; a < 20; a++)
18+
for (int a = -20; a < 20; a++)
1919
{
20-
for (int b = 0; b < 20; b++)
20+
for (int b = -20; b < 20; b++)
2121
{
22-
MathLib.FloorSum(n, m, a, b).Should().Be(FloorSumNative(n, m, a, b));
22+
var expected = FloorSumNative(n, m, a, b);
23+
MathLib.FloorSum(n, m, a, b).Should()
24+
.Be(expected, "FloorSum({0},{1},{2},{3}) should be {4}", n, m, a, b, expected);
2325
}
2426
}
2527
}

Test/AtCoderLibrary.Test/Utils/MathUtil.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ public static long FloorSumNative(long n, long m, long a, long b)
4141
long sum = 0;
4242
for (long i = 0; i < n; i++)
4343
{
44-
sum += (a * i + b) / m;
44+
long z = a * i + b;
45+
sum += (z - InternalMath.SafeMod(z, m)) / m;
4546
}
4647
return sum;
4748
}
4849

49-
5050
public static List<int> Factors(int m)
5151
{
5252
var result = new List<int>();

0 commit comments

Comments
 (0)