• bzoj2154 Crash的数字表格


    2154: Crash的数字表格

    Time Limit: 20 Sec  Memory Limit: 259 MB
    Submit: 4549  Solved: 1643
    [Submit][Status][Discuss]

    Description

    今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数a和b,LCM(a, b)表示能同时被a和b整除的最小正整数。例如,LCM(6, 8) = 24。回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张N*M的表格。每个格子里写了一个数字,其中第i行第j列的那个格子里写着数为LCM(i, j)。一个4*5的表格如下: 1 2 3 4 5 2 2 6 4 10 3 6 3 12 15 4 4 12 4 20 看着这个表格,Crash想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当N和M很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和mod 20101009的值。

    Input

    输入的第一行包含两个正整数,分别表示N和M。

    Output

    输出一个正整数,表示表格中所有数的和mod 20101009的值。

    Sample Input

    4 5

    Sample Output

    122
    【数据规模和约定】
    100%的数据满足N, M ≤ 10^7。
    分析:比较难的一道题,题目要求,其中lcm(i,j) = i*j / gcd(i,j),带入式子中可以得到:,枚举i,j是不划算的,设d = gcd(i,j),枚举d,并设F(x,y)表示,gcd(i,j)=1的情况比较好处理,有较为快捷的方法可以得到,那么答案就是原理就是gcd(i,j) = d ---> gcd(i/d,j/d) = 1,F实际上就是i/d和j/d的乘积和,最后i/d,j/d都要分别乘上d才是ij,约掉一个d,可以得到:.
          接下来考虑怎么求F函数,当F(x,y)中的x,y固定后,比较麻烦的就是gcd(i,j) = 1的这个限制了,没关系,还是可以沿用上面的思路,将gcd(i,j) = d变成gcd(i/d,j/d) = 1,令sum(x,y) = ,那么带入到式子中就可以得到,.
           那么维护两个前缀和,两个式子都可以在根号时间内得到,总的复杂度就是O(n).
           这道题我又WA,又T,又MLE的,总结一下原因:MLE是因为数组开的太大了,当空间接近极限的时候能用int就不要用long long,能用bool就不要用int,有一些数组能适当的减小范围,比如筛出来的质数数组,一般比maxn小两个数量级. WA是因为模数的原因,一个函数忘了取模的,结果总是个负数.需要注意的是减法一定要+模数再取模. TLE是因为预处理的上限设定太高了,这道题就一组数据,把min(n,m)当作上限就好了......
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    
    const ll maxn = 10000000, mod = 20101009;
    
    int prime[3000000], tot, mo[maxn + 10], n, m;
    bool vis[maxn + 10];
    ll sum1[maxn + 10], sum2[maxn + 10];
    
    void init()
    {
        mo[1] = 1;
        for (ll i = 2; i <= min(n,m); i++)
        {
            if (!vis[i])
            {
                prime[++tot] = i;
                mo[i] = -1;
            }
            for (ll j = 1; j <= tot; j++)
            {
                ll t = prime[j] * i;
                if (t > min(n,m))
                    break;
                vis[t] = 1;
                if (i % prime[j] == 0)
                {
                    mo[t] = 0;
                    break;
                }
                mo[t] = -mo[i];
            }
        }
        for (ll i = 1; i <= min(n,m); i++)
        {
            sum1[i] = sum1[i - 1] + i;
            sum2[i] = sum2[i - 1] + i * i * mo[i] % mod;
            sum1[i] %= mod;
            sum2[i] %= mod;
        }
    }
    
    ll sum(ll x, ll y)
    {
        return ((x * (x + 1) / 2) % mod) * ((y * (y + 1) / 2) % mod) % mod;
    }
    
    ll F(ll x, ll y)
    {
        ll res = 0, last = 0;
        for (ll i = 1; i <= min(x, y); i = last + 1)
        {
            last = min(x / (x / i), y / (y / i));
            res += (((sum2[last] - sum2[i - 1] + mod) % mod) * sum(x / i, y / i)) % mod;
            res %= mod;
        }
        return res;
    }
    
    ll solve()
    {
        ll res = 0, last = 0;
        for (ll i = 1; i <= min(n, m); i = last + 1)
        {
            last = min(n / (n / i), m / (m / i));
            res += (((sum1[last] - sum1[i - 1] + mod) % mod) * F(n / i, m / i)) % mod;
            res %= mod;
        }
        return res;
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        init();
        printf("%lld
    ", solve() % mod);
    
        return 0;
    }
  • 相关阅读:
    《那些年啊,那些事——一个程序员的奋斗史》——81
    《那些年啊,那些事——一个程序员的奋斗史》——83
    《那些年啊,那些事——一个程序员的奋斗史》——80
    《那些年啊,那些事——一个程序员的奋斗史》——82
    《那些年啊,那些事——一个程序员的奋斗史》——81
    《那些年啊,那些事——一个程序员的奋斗史》——82
    《那些年啊,那些事——一个程序员的奋斗史》——82
    网络学习杂七杂八
    字典类的代码的学习
    SNMP++ 编译记录
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7943270.html
Copyright © 2020-2023  润新知