XTU 1203 A simple problem (2014 湖南湘潭邀请赛 A题)(数学)

A simple problem

Accepted : 27   Submit : 296
Time Limit : 15000 MS   Memory Limit : 655360 KB

Problem Description

There is a simple problem. Given a number N. you are going to calculate N%1+N%2+N%3+...+N%N.

Input

First line contains an integer T, there are T(1≤T≤50) cases. For each case T. The length N(1≤N≤1012).

Output

Output case number first, then the answer.

Sample Input

1
5

Sample Output

Case 1: 4


Source

daizhenyang

    解题报告:
    一道不是特别难想的题,但是要注意很多细节。
    首先,求N模除1-N的和,N<=10^12,暴力是不可能的。我们可以找下规律。N%N=0,N%(N-1)=1, N%(N-2)=2,(N较大时)。
    可以发现,N 对 floor(N/2)+1到N的模除的结果依次减少1,模除的结果形成了等差数列。这是我们可以求的。
    同理可以求得(N/3, N/2)区间,(N/4, N/3)区间的模除和。
    这样岂不是要求到N/N?其实当(N/M)较小时,我们直接暴力求出(0, N/M)这段的模除和。
    算法基本就是这样。复杂度O(sqrt(n))。
    因为N很大,模除和超出了long long类型,需要使用大数。
    比赛的时候思路基本就是这样,我敲的时候缺犯了一个错误:
    后面的暴力和不会超过long long,而我用大数直接加上去……多一题就可以拿第二了,少了这题就是银首……
    贴个代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <string>
#include <set>
#include <map>
#include <queue>
#include <vector>
using namespace std;
typedef long long LL;
#define fff(i, n, m) for(int i=(n);i<=(m);i++)
#define ff(i, n) for(int i=0;i<(n);i++)
#define fout freopen
void work();
int main()
{
    #ifdef ACM
    fout("in.txt", "r", stdin);
    #endif
    work();
}

/*==============================*/

class Bignum
{
public:

    int a[10];
    int len;

    Bignum()
    {
        memset(a, 0, sizeof(a));
        len = 1;
    }

    Bignum(LL num)
    {
        memset(a, 0, sizeof(a));
        len = 0;

        while(num)
        {
            a[len++] = num%10000, num/=10000;
        }

        len = max(1, len);
    }

    Bignum(const Bignum & b)
    {
        memset(a, 0, sizeof(a));
        len = b.len;
        ff(i, len) a[i]=b.a[i];
    }

    int & operator[](int i)
    {
        return a[i];
    }

    Bignum operator+(const Bignum & b)
    {
        Bignum c;
        c.len = max(len, b.len);

        ff(i, c.len)
        {
            c[i] += a[i] + b.a[i];
            if(c[i]>10000)
                c[i+1]+=c[i]/10000, c[i]%=10000;
        }

        if(c[c.len]) c.len++;
        return c;
    }

    Bignum operator*(const Bignum & b)
    {
        Bignum c;
        c.len = len+b.len-1;

        ff(i, len) ff(j, b.len)
        {
            c[i+j] += a[i]*b.a[j];
            if(c[i+j]>10000)
                c[i+j+1]+=c[i+j]/10000, c[i+j]%=10000;
        }

        if(c[c.len]) c.len++;

        return c;
    }

    void print()
    {
        printf("%d", a[len-1]);

        for(int i=len-2;i>=0;i--)
            printf("%04d", a[i]);
    }
};

void work()
{
    int T;
    scanf("%d", &T);

    fff(cas, 1, T)
    {
        LL n;
        scanf("%I64d", &n);

        LL sta = n, end = n;

        Bignum ans;

        LL t = 1;
        LL m = sqrt(n+0.0);
        m = (LL)(m * 7);    // 个人感觉相对较快
        while(sta > m)
        {
            end = sta;
            sta = n/(t+1);

            LL r = n%end;
            LL num = end - sta;
            LL n1=num, n2=num-1;
            if(n1%2==0) n1/=2; else n2/=2;

            ans = ans + Bignum(r*num) + Bignum(n1)*Bignum(n2*t);

            t++;
        }

        LL rr = 0;
        while(sta)
        {
            rr+=n%sta;
            sta--;
        }

        ans = ans+Bignum(rr);

        printf("Case %d: ", cas);
        ans.print();
        puts("");
    }
}


你可能感兴趣的:(优化,数学)