hrbust 1857 koko的能量值公式【思维+二分】好题!好题!

koko的能量值公式
Time Limit: 3000 MSMemory Limit: 32768 K
Total Submit: 25(13 users)Total Accepted: 15(10 users)Rating: Special Judge: No
Description

极之剑圣koko举起了胜利誓约之剑。

Koko面色凝重的盯着麦斯麦提克的大魔王AK_PUSH,喝到:“狂风,赐我予权柄!”。这是一门传说中的剑术,可以控制气流进行攻击。只见爆裂的气流散射状喷发出去,犹如一道道拉长了的白线,在空中划出无数美妙的弧线直逼大魔王AK_PUSH而去。。。。。。

咳咳。。小胖子koko最近在学幼稚园编程。。。感觉到单纯的学习很无聊,于是koko打算开发一款游戏,名叫幼稚园之勇者战魔王(咳咳)。

现在koko遇到了一个难题,他为游戏中剑圣职业开发了一个新技能“狂风权柄”,它的伤害计算公式如下:

long long H( int n ) {

    long long res = 0;

    for( int i = 1; i <= n; i++ )

        res = res + n / i;

    return res;

}

其中n是使用的能量。由于按照上述公式直接计算伤害太慢了,所以koko又来请教聪明的你啦~你的任务是帮koko设计一个程序,可以比较高效的计算出“狂风权柄”的伤害值。


Input

输入以T开始,代表测试样例的个数(T<=1000)

每组样例包括一个整数:n(n<2^31),代表能量值


Output

每一组样例,输出H(N)

Sample Input

11

1

2

3

4

5

6

7

8

9

10

2147483647
Sample Output

Case 1: 1

Case 2: 3

Case 3: 5

Case 4: 8

Case 5: 10

Case 6: 14

Case 7: 16

Case 8: 20

Case 9: 23

Case 10: 27

Case 11: 46475828386


Author
小伙伴们@哈商大

思路:


1、考虑这样一个问题,对应n/i的每个单项的值:


我们发现,当随着i越来越大的时候,其单项的值的变化是-1的。

那么考虑什么时候开始才能使得随着i越来越大的时候,其单项的值的变化是-1的:

①我们在求一个数n是不是素数的时候,我们通常从1扫到sqrt(n);

②因为如果sqrt(n)以内有一个因子数zz,那么对应n/zz也是一个因子数,不需要继续判断了。

③那么考虑如果zz是一个因子数(zz<=sqrt(n)),那么对应n/zz和n/zz+1可能是差值大于1的,但是对应到n/n/zz和n/n/zz+1,其差值是一定小于等于1的。

④考虑到这里,大家不难想到,对应从1扫到sqrt(n)的单项(n/i)的相邻两个数的差值变化可能是大于1的,但是对应从sqrt(n)+1扫到n的单项(n/i)的相邻两个数的差值变化是一定小于等于1的。


2、那么我们最终可以这样来做这个题:

①前sqrt(n)个单项,我们直接累加起来。

②然后求出下一单项的值记做tmp;此时tmp不会很大(手动测试当n为2147483647的时候,tmp=46340),那么我们接下来对应区间内相邻两项差值为0的范围进行确定。那么我们此时二分查找最右边的等于tmp的位子,对应区间大小记做len,那么sum+=len*tmp;

③每一次二分结束之后,区间左端点的值=此时最右边的等于tmp的位子+1;tmp--;

④时间复杂度姑且可以算作:O(tmp*logn)(当然一定比这个值小);


Ac代码:


#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
#define ll long long int
int main()
{
    int kase=0;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll n;
        scanf("%lld",&n);
        long long sum=0;
        ll i;
        for(i=1;i<=sqrt(n);i++)
        {
            sum+=n/i;
        }
        ll tmp=n/i;
        while(tmp)
        {
            ll l=i;
            ll r=n;
            ll ans;
            ll mid;
            while(r-l>=0)
            {
                mid=(l+r)/2;
                if(n/mid==tmp)
                {
                    l=mid+1;
                    ans=mid;
                }
                else
                {
                    r=mid-1;
                }
            }
            sum+=tmp*(ans-i+1);
            i=ans+1;
            tmp--;
        }
        printf("Case %d: ",++kase);
        printf("%lld\n",sum);
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值