• [BZOJ 2154] Crash的数字表格


    2154: Crash的数字表格

    Time Limit: 20 Sec  Memory Limit: 259 MB
    Submit: 1337  Solved: 531
    [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 ≤ 107。

    HINT

    Source

    数论

    【题解】

    用莫比乌斯繁衍(题解均省略取模运算)

    设sum2(i)=i*(i+1)/2

    设函数sum(i,j)=sum2(i)*sum2(j)

    那么,ans=sigma (D=1 to min(n,m)) sum(n/d,m/d)* (sigma (i|D) Di*mu[i])

    (这是可以推出来的)

    然后呢,YSY聚聚告诉我了一个方法:

    线性筛筛素数,记录i最小素数因子为mz[i]

    然后,设f[D] 为 (sigma (i|D) Di*mu[i]),那么,

    f[D]=f[D/mz[i]](mz[i/mz[i]]==mz[i])

    f[D]=f[D/mz[i]]*(1-mz[i]) (else)

    那么,即可在O(n)时间内处理

    可以用前缀和维护f,则在O(sqrt n)之内处理完

    需要强调的是!!!!!!!

    括号啊括号!!long long啊long long!!!

    WA了10几次,精度爆炸了

    首先是sum,不能连乘3次!!

    long long 强制转换要放内部!

    唉,ysy一遍A,我WA了10几次,TAT

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int prime[1000010];
     4 int tot,mz[10000010];
     5 int f[10000010],n,m;
     6 long long ans;
     7 const int mod=20101009;
     8 long long sum(int ki,int kj) {
     9     return ((((long long)ki*(ki+1)/2)%mod)*(((long long)kj*(kj+1)/2)%mod))%mod;
    10 }
    11 int main() {
    12     //freopen("a.out","w",stdout);
    13     scanf("%d%d",&n,&m);
    14     if (n>m) {
    15         int t=n;
    16         n=m;
    17         m=t;
    18     }
    19     for (int i=2;i<=n;++i) {
    20         if (!mz[i]) {
    21             prime[++tot]=i;
    22             mz[i]=i;
    23         }
    24         for (int j=1;j<=tot&&i*prime[j]<=n;++j) {
    25             mz[i*prime[j]]=prime[j];
    26             if(i%prime[j]==0) break;
    27         }
    28     }
    29     //for (int i=1;i<=tot;++i) cout<<prime[i]<<endl;
    30     //for (int i=1;i<=n;++i) cout<<mz[i]<<endl;
    31     f[1]=1;
    32     for (int i=2;i<=n;++i)
    33         if(mz[i/mz[i]]==mz[i]) f[i]=f[i/mz[i]]%mod;
    34         else f[i]=((long long)(f[i/mz[i]]*(1-mz[i])))%mod;
    35     for (int i=1;i<=n;++i) ans=(ans+(sum(n/i,m/i)*(((long long)f[i]*i)%mod)))%mod;
    36     printf("%lld
    ",(ans%mod+mod)%mod);
    37     return 0;
    38 }
    View Code

    上面的代码为O(n)算法,8988ms

    下面我们可以用前缀和来维护,O(sqrt n) 4989ms

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int prime[1000010];
     4 int tot,mz[10000010];
     5 int f[10000010],n,m;
     6 long long ans;
     7 const int mod=20101009;
     8 long long sum(int ki,int kj) {
     9     return ((((long long)ki*(ki+1)/2)%mod)*(((long long)kj*(kj+1)/2)%mod))%mod;
    10 }
    11 int main() {
    12     //freopen("a.out","w",stdout);
    13     scanf("%d%d",&n,&m);
    14     if (n>m) {
    15         int t=n;
    16         n=m;
    17         m=t;
    18     }
    19     for (int i=2;i<=n;++i) {
    20         if (!mz[i]) {
    21             prime[++tot]=i;
    22             mz[i]=i;
    23         }
    24         for (int j=1;j<=tot&&i*prime[j]<=n;++j) {
    25             mz[i*prime[j]]=prime[j];
    26             if(i%prime[j]==0) break;
    27         }
    28     }
    29     //for (int i=1;i<=tot;++i) cout<<prime[i]<<endl;
    30     //for (int i=1;i<=n;++i) cout<<mz[i]<<endl;
    31     f[1]=1;
    32     for (int i=2;i<=n;++i)
    33         if(mz[i/mz[i]]==mz[i]) f[i]=f[i/mz[i]];
    34         else f[i]=((long long)f[i/mz[i]]*(1-mz[i]))%mod;
    35     for (int i=2;i<=n;++i)
    36         f[i]=((long long)f[i]*i+f[i-1])%mod;
    37     int j=0;
    38     for (int i=1;i<=n;i=j+1) {
    39         j=min(n/(n/i),m/(m/i));
    40         ans=(ans+(sum(n/i,m/i)*(long long)(f[j]-f[i-1])%mod))%mod;
    41     }
    42     printf("%lld
    ",(ans+mod)%mod);
    43     return 0;
    44 }
    View Code
  • 相关阅读:
    稀疏自编码器一览表
    ZOJ 3886 Nico Number(筛素数+Love(线)Live(段)树)
    K好数(DP)
    【BZOJ4025】二分图
    又一次认识java(七) ---- final keyword
    二分查找
    从朴素贝叶斯分类器到贝叶斯网络(下)
    最近感到深深的绝望,感觉自己太菜了
    leetcode No.19 删除链表的倒数第N个节点 (python3实现)
    leetcode No.94 二叉树的中序遍历 (python3实现)
  • 原文地址:https://www.cnblogs.com/TonyNeal/p/bzoj2154.html
Copyright © 2020-2023  润新知