生日蛋糕 HBCPC2022 7-2

题目大意:对于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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值