(1988)FZU

#include<iostream>
#include<cstdio>
#include<string.h>
#include<string>
#include<set>
#include<algorithm>
#include<cmath>


#define ll __int64
#define MAX 60000000
using namespace std;


/*

类型:数论加二分

题意:问你能否找到前K项的因子和等于M,如果有输出K,如果没有输出 impossible!
分析:题目中(1 <= M <= 10^9)我们首先暴力出来上界是多少,然后根据这个知识我们来二分X,[1,上界],判断是否等于M,不断二分得出答案
数论知识:计算 [1, X]区间内所有数字的因子个数之和这个等价于X / 1 + X / 2 + ... + X / X
假如拿13举例子


13/1,sum+= 13*1, i++;


13/2,sum+= 6*1, i++;


13/3,sum+= 4*1, i++;


13/4,sum+= 3*1, i++;


13/5,sum+= 2*2, i=k+1;,k=13/2=6,结果是2的数中最大是6,最小是5)


13/7,sum+= 1*6, i=k+1;,k=13/1=13,结果是1的数中最大是13,最小是7)


第二种方法,其实不用除到X,除到sqrt(x)就可以了,这里跟素数晒那个思想差不多····
*/
ll ok1(ll x)
{
    ll sum = 0;
    for(ll i = 1; i<=x;)
    {
        ll w = x/i;
        ll m = x/w;
        if(m==i)
        {
            sum+=w;
            i++;
        }
        else
        {
            sum+=(m - i + 1)*w;
            i = m + 1;
        }
    }
    return sum;
}
ll ok2(ll x)
{
    ll sum = 0;
    ll y = (int)sqrt(x*1.0);
    for(ll i = 1;i<=y;i++)
    {
        sum+=x/i;
    }
    return (sum<<1)-y*y;
}
int main()
{
    int t;
    ll flag;
    scanf("%d",&t);
    for(int i = 1; i<=t; i++)
    {
        ll m;
        scanf("%I64d",&m);
        ll l = 0;
        ll r = 60000000;
        ll mid;
        flag = -1;
        while(l<=r)
        {
            mid = (l+r)>>1;
            if(ok2(mid)==m)
            {
                flag = mid;
                break;
            }
            else if(ok2(mid)>m)
                r = mid - 1;
            else
                l = mid + 1;
        }
        //cout<<flag<<endl;
        if(flag!=-1)
        {
            printf("Case %d: %I64d\n",i,flag);
        }
        else
        {
            printf("Case %d: impossible!\n",i);
        }
    }
    return 0;
}

你可能感兴趣的:((1988)FZU)