Project Euler 688

Project Euler 688

题目

Piles of Plates

We stack \(n\) plates into \(k\) non-empty piles where each pile is a different size. Define \(f(n,k)\) to be the maximum number of plates possible in the smallest pile. For example when \(n = 10\) and \(k = 3\) the piles \(2,3,5\) is the best that can be done and so \(f(10,3) = 2\). It is impossible to divide \(10\) into \(5\) non-empty differently-sized piles and hence \(f(10,5) = 0\).

Define \(F(n)\) to be the sum of \(f(n,k)\) for all possible pile sizes \(k\ge 1\). For example \(F(100) = 275\).

Further define \(S(N) = \displaystyle\sum_{n=1}^N F(n)\). You are given \(S(100) = 12656\).

Find \(S(10^{16})\) giving your answer modulo \(1\,000\,000\,007\).

解决方案

\(N=10^{16}.\)

如果\(n\)个盘子放成\(k\)堆,那么一种贪心的方法则是先摆成\(1,2,3,\dots,k-1,k\)\(k\)堆,接下来将剩下的\(n-\dfrac{k(k+1)}{2}\)碟子再均分到这\(k\)堆,如果剩下的碟子依然不能均分,那么全部都分给最大的那堆。因此不难写出函数\(f\)为:

\[ f(n,k)= \left \{\begin{aligned} &0 & & \text{if}\quad n<\dfrac{k(k+1)}{2} \\ &1+\left\lfloor\dfrac{n}{k}-\dfrac{k+1}{2}\right\rfloor & & \text{else} \end{aligned}\right. \]

不过,这个式子其实对解题并没有太大的帮助。

\(S\)的定义式子进行改写:

\[S(N)=\sum_{n=1}^N\sum_{k\ge 1}f(n,k)=\sum_{1\le \frac{k(k+1)}{2}\le N}\sum_{n=\frac{k(k+1)}{2}}^{N}f(n,k)\]

本质上,是将\(n,k\)的枚举顺序改变了。

式子\(\sum_{n=\frac{k(k+1)}{2}}^{N}f(n,k)\)的值可以以\(O(1)\)的时间复杂度进行计算。因为按照此时\(n\)枚举顺序,\(f(n,k)\)将会是连续\(k\)\(1\)\(k\)\(2\),……考虑分块然后使用等差数列求和进行计算。

\(x=N-\dfrac{k(k+1)}{2}+1\)\(q=\left\lfloor\dfrac{x}{k}\right\rfloor,r=x\%k\),那么分块后计算得\(\sum_{n=\frac{k(k+1)}{2}}^{N}f(n,k)=k\cdot\dfrac{q\cdot(q+1)}{2}+(q+1)\cdot r\)

最终枚举\(k\)并将这些值相加即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const ll Q=1e16;
ll mod=1e9+7;
ll inv2=(mod+1)>>1;
int main(){
ll ans=0;
for(ll k=1;;k++){
ll w=k*(k+1)/2;
if(w>Q) break;
ll t=Q-w+1;
ll q=t/k%mod,r=t%k;
ans=(ans+k*q%mod*(q+1)%mod*inv2%mod+(q+1)*r%mod)%mod;
}
printf("%lld\n",ans);
}