• 洛谷 P3327 [SDOI2015]约数个数和 || Number Challenge Codeforces


    https://www.luogu.org/problemnew/show/P3327

    不会做。

    去搜题解...为什么题解都用了一个奇怪的公式?太奇怪了啊。。。

    公式是这样的:

    $d(xy)=sum_{i|x}sum_{j|y}[(i,j)=1]$

    证明:(转自:https://23613.blog.luogu.org/solution-p3327

    考虑一个质因子p,设x中p的指数为a,y中p的指数为b(指质因数分解结果中指数),那么根据因数个数定理,这个质因子对式子左边的贡献(指使得答案乘上的值)是a+b+1;在式子右边,要么i中p的指数为0且j中p的指数为1~b,要么j中p的指数为0且i中p的指数为1~a,要么i和j中p的指数均为0(为了保证i,j互质),所以p的贡献是(b+a+1)。那么左右两边就相等了...

    没办法了,背公式吧...找不到别的做法

    答案要求的式子ans可以化为$sum_{i=1}^nsum_{j=1}^m[(i,j)=1]{lfloor}frac{n}{i}{ floor}{lfloor}frac{m}{j}{ floor}$

    反演一下,得到$ans=sum_{k=1}^mmu(k)sum_{i=1}^{{lfloor}frac{n}{k}{ floor}}sum_{j=1}^{{lfloor}frac{m}{k}{ floor}}{lfloor}frac{n}{ik}{ floor}{lfloor}frac{m}{jk}{ floor}$(不妨设n<=m)

    设$f(x)=sum_{i=1}^x{lfloor}frac{x}{i}{ floor}$,这个显然可以n*sqrt预处理出来

    $ans=sum_{k=1}^mmu(k)f({lfloor}frac{n}{k}{ floor})f({lfloor}frac{m}{k}{ floor})$

    那么原式也可以简单的在一次询问sqrt的时间内求出

    本以为就这样子A掉了,结果...T飞了

    不管了。。O3开开A掉

    网上找了一下,有一个方法卡常数:可以发现f函数是约数个数函数的前缀和,因此可以筛出约数个数然后直接前缀和,这样预处理就是O(n)的

     1 #pragma GCC optimize(3)
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<vector>
     6 using namespace std;
     7 #define fi first
     8 #define se second
     9 #define mp make_pair
    10 #define pb push_back
    11 typedef long long ll;
    12 typedef unsigned long long ull;
    13 typedef pair<int,int> pii;
    14 ll prime[20100],len,ff[50100],mu[50100];
    15 bool nprime[50100];
    16 ll ans,n,m;
    17 int main()
    18 {
    19     ll i,j,k,T;
    20     mu[1]=1;
    21     for(i=2;i<=50000;i++)
    22     {
    23         if(!nprime[i])    prime[++len]=i,mu[i]=-1;
    24         for(j=1;j<=len&&i*prime[j]<=50000;j++)
    25         {
    26             nprime[i*prime[j]]=1;
    27             if(i%prime[j]==0)    {mu[i*prime[j]]=0;break;}
    28             else    mu[i*prime[j]]=-mu[i];
    29         }
    30     }
    31     for(i=1;i<=50000;i++)    mu[i]+=mu[i-1];
    32     for(k=1;k<=50000;k++)
    33     {
    34         for(i=1;i<=k;i=j+1)
    35         {
    36             j=min(k,k/(k/i));
    37             ff[k]+=(j-i+1)*(k/i);
    38         }
    39     }
    40     //for(i=1;i<=10;i++)    printf("%lld
    ",ff[i]);
    41     scanf("%lld",&T);
    42     while(T--)
    43     {
    44         scanf("%lld%lld",&n,&m);
    45         if(n>m)    swap(n,m);
    46         ans=0;
    47         for(i=1;i<=n;i=j+1)
    48         {
    49             j=min(n,min(n/(n/i),m/(m/i)));
    50             ans+=(mu[j]-mu[i-1])*ff[n/i]*ff[m/i];
    51         }
    52         printf("%lld
    ",ans);
    53     }
    54     return 0;
    55 }

    http://codeforces.com/contest/235/problem/E

    是不是感觉非常像?

    事实上,不仅式子像,结论也是一样的。。。好神奇啊

    结论是:$d(xyz)=sum_{i|x}sum_{j|y}sum_{k|z}[(i,j)=1][(j,k)=1][(i,k)=1]$

    证明也是类似的;貌似可以推广到更高维(?没试过)

    同样的,$ans=sum_{i=1}^asum_{j=1}^bsum_{k=1}^c[(i,j)=1][(j,k)=1][(i,k)=1]{lfloor}frac{a}{i}{ floor}{lfloor}frac{b}{j}{ floor}{lfloor}frac{c}{k}{ floor}$

    耶,我又不会了,反演嘛式子推不清楚,乱搞嘛搞不出来

    到网上找,发现一个技巧:如果只是要求XXX=1,用$sum_{d|k}mu(d)=[k=1]$去代比直接反演更方便

    有了这样一个做法:

    枚举i,搞出所有可能的j,k(指与i互质且在范围内),分别放入数组aj,ak

    那么就需要从aj,ak内分别取一个元素x,y,如果(x,y)==1则可以计算贡献

    也就是要计算$sum_{j{in}aj}sum_{k{in}ak}[(j,k)=1]{lfloor}frac{b}{j}{ floor}{lfloor}frac{c}{k}{ floor}=sum_{j{in}aj}sum_{k{in}ak}sum_{d|(j,k)}mu(d){lfloor}frac{b}{j}{ floor}{lfloor}frac{c}{k}{ floor}$

    考虑对每一个d统计贡献,复杂度n^2*log

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<vector>
     5 using namespace std;
     6 #define fi first
     7 #define se second
     8 #define mp make_pair
     9 #define pb push_back
    10 typedef long long ll;
    11 typedef unsigned long long ull;
    12 typedef pair<int,int> pii;
    13 int prime[2010],len,mu[2010];
    14 bool nprime[2010];
    15 int a,b,c,mm;
    16 unsigned ans,a1,n1,n2;
    17 bool aj[2010],ak[2010];
    18 int main()
    19 {
    20     int i,j,k,d;
    21     mu[1]=1;
    22     for(i=2;i<=2000;i++)
    23     {
    24         if(!nprime[i])    prime[++len]=i,mu[i]=-1;
    25         for(j=1;j<=len&&i*prime[j]<=2000;j++)
    26         {
    27             nprime[i*prime[j]]=1;
    28             if(i%prime[j]==0)    {mu[i*prime[j]]=0;break;}
    29             else    mu[i*prime[j]]=-mu[i];
    30         }
    31     }
    32     scanf("%d%d%d",&a,&b,&c);
    33     mm=max({a,b,c});
    34     for(i=1;i<=a;i++)
    35     {
    36         a1=0;
    37         for(j=1;j<=b;j++)
    38             aj[j]=(__gcd(i,j)==1);
    39         for(k=1;k<=c;k++)
    40             ak[k]=(__gcd(i,k)==1);
    41         for(d=1;d<=mm;d++)
    42         {
    43             n1=n2=0;
    44             for(j=d;j<=b;j+=d)
    45                 n1+=aj[j]*(b/j);
    46             for(k=d;k<=c;k+=d)
    47                 n2+=ak[k]*(c/k);
    48             a1+=n1*n2*mu[d];
    49         }
    50         ans+=a1*(a/i);
    51     }
    52     printf("%u",ans%1073741824);
    53     return 0;
    54 }
  • 相关阅读:
    React Native从入门到放弃之坑总结
    React Native从入门到放弃之环境搭建
    flex弹性布局语法介绍及使用
    JavaScript ES6中export及export default的区别
    解决webstorm本地IP访问页面出错的问题
    UEFI+GPT与BIOS+MBR各自有什么优缺点?
    解决U盘拷贝时提示文件过大问题(不能拷贝超过4个g的文件)
    U盘安装原版Win7或Win8教程
    安装win8/win10提示无法在驱动器0分区上安装windows解决方法
    idea 编译报错 未结束的字符串字面值,非法的类型开始
  • 原文地址:https://www.cnblogs.com/hehe54321/p/9374887.html
Copyright © 2020-2023  润新知