题目大意:对于m属于1到n,分别需要找到一个最大的k使得k*m<=n,问不同的k有多少种,记为fn,现给出一整数nn,求n属于1到n,fn的和
1<=t<=1e5;1<=nn<=1e9
思路:通过打表可以发现,nn属于1到25时的答案分别为1,3,5,8,11,15,19,23,28,33,38,44,50,56,62,69,76,83,90,98,106,114,122,130,139可以发现其分别为长度为2公差为2的等差数列接一个长度为2公差为3的等差数列,接一个长度为3公差为4的等差数列。。。同时通过进一步打表可以发现一共只有63243个等差数列且所有数均不超过log long范围,所以我们可以在打表时记录等差数列的头元素同时记录公差和头元素对应的n,然后每输入一个nn,直接二分找到他属于第几个等差数列,取出头元素和公差,然后答案就是(nn-n)*公差,再对1e9+7取模
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N =1e6+10,MOD=1e9+7;
typedef long long ll;
struct nnnn
{
ll a, b, x,cnt;//储存等差数列的长度、公差、头元素、头元素的位置
}ans[N];
bool check(int x,int n)
{
return ans[x].cnt<=n;//二分找到是第几个等差数列
}
int main()
{
int cnt = 1;
ans[1].x = 1;
ans[1].cnt = 1;
ll a = 2, b = 2;
int flag = 0;
int temp;
for (int i = 2; cnt <= 1000000000; i++)
{//打表记录每个等差数列的信息
ans[i].x = ans[i - 1].x + a * b;//下一个等差数列的头元素=上一个头元素+公差*长度
ans[i].a = a;//长度
ans[i].b = b;//公差
cnt += a;//记录数的总数
ans[i].cnt = cnt;
b++;//每个等差数列的公差递增
flag = 1 - flag;
if (!flag)
a++;//每两个等差数列长度递增
}
int t;
cin >> t;
while (t--)
{
int n;
scanf_s("%d", &n);
int l = 1, r = 63243, mid;
int ret;
while (l <= r)
{
mid = (l + r) >> 1;
if (check(mid, n))
{
ret = mid;
l = mid + 1;
}
else
r=mid-1;
}
a = ans[ret+1].a, b = ans[ret+1].b;
ll out = ans[ret].x;//所在等差数列的头元素
flag = 0;
out += (n - ans[ret].cnt) * b;//中间的项数*公差
printf("%lld\n", out%MOD);//最后输出时取模即可
}
return 0;
}