• BZOJ 2301 HAOI2011 Problem b 莫比乌斯反演


    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2301

    题意概述:

      对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k。

      1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000.

    分析:

      这实际上就是莫比乌斯反演的裸题!

      但是我为什么要专门发出来呢?

      因为我想总结一下代码的问题和一般思路......

      首先观察题目询问,可以发现询问可以用矩形前缀和来回答。于是问题简化为给出a,b,k,求gcd(x,y)=k的数对数量,1<=x<=a,1<=y<=b。

      我们令函数,这就是我们的计算目标,但是显然这个东西求起来非常的麻烦啊!!评测不能承受之慢啊!!!

      于是我们考虑莫比乌斯反演,构造一个更加简单易求的函数F(k),F(k)可以由f(d)(F有两种考虑方向,k|d或者d|k)得到。

      考虑两种构造的方式,最后构造出来的F(k)意义为一定范围内k|gcd(x,y)的(x,y)的对数,

      其中F(k)是非常好求的,

      根据莫比乌斯反演,我们得到:,即

      可以发现其中d/k的值一定是连续的,不妨令n<=m,那么我们可以把这个柿子变成:

      同时我们发现n最多有2*sqrt(n)种被整除的结果,于是我们只需要维护一个μ的前缀和就可以大幅提高询问的效率,利用经典方法在O(sqrt(N))的时间内回答单组询问(具体讲解见代码注释)。

      时间复杂度O(N*sqrt(N))。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstdlib>
     5 #include<algorithm>
     6 #include<cmath>
     7 #include<queue>
     8 #include<set>
     9 #include<map>
    10 #include<vector>
    11 #include<cctype>
    12 using namespace std;
    13 const int maxn=50005;
    14 const int maxp=5200;
    15 typedef long long LL;
    16 
    17 int N,a,b,c,d,k;
    18 int pri[maxp],tot,mu[maxn],sum[maxn]; bool ntp[maxn];
    19 
    20 void get_mu()
    21 {
    22     ntp[0]=ntp[1]=1,mu[1]=1;
    23     for(int i=2;i<=50000;i++){
    24         if(!ntp[i]) pri[++tot]=i,mu[i]=-1;
    25         for(int j=1;j<=tot&&1ll*pri[j]*i<=50000;j++){
    26             ntp[pri[j]*i]=1;
    27             if(i%pri[j]==0){ mu[i*pri[j]]=0; break; }
    28             mu[i*pri[j]]=-mu[i];
    29         }
    30     }
    31     for(int i=1;i<=50000;i++) sum[i]=sum[i-1]+mu[i];
    32 }
    33 LL calc(int n,int m)
    34 {
    35     if(n>m) swap(n,m);
    36     n/=k,m/=k;
    37     LL re=0;
    38     for(int i=1,last=0;i<=n;i=last+1){
    39         last=min(n/(n/i),m/(m/i));//令x=n/i,那么可以发现n/x=max{i|n/i=x},只要n/i和m/i中间有一个变化了F(d)就会改变,所以取min。
    40         re+=1ll*(sum[last]-sum[i-1])*(n/i)*(m/i);//因为n最多被乘除的结果最多有2*sqrt(N)种,所以单次询问复杂度O(sqrt(N))。
    41     }
    42     return re;
    43 }
    44 int main()
    45 {
    46     freopen("test.in","r",stdin);
    47     freopen("test.out","w",stdout);
    48     get_mu();
    49     scanf("%d",&N);
    50     for(int i=1;i<=N;i++){
    51         scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
    52         printf("%lld
    ",calc(b,d)-calc(a-1,d)-calc(b,c-1)+calc(a-1,c-1));
    53     }
    54     return 0;
    55 }
    View Code
  • 相关阅读:
    第十二章 基本数据类型
    第十一章 变量名的力量
    第十章 使用变量的一般事项
    第九章 伪代码编程过程
    第八章 防御式编程
    JMeter简介
    第七章 高质量的子程序
    第六章 可以工作的类
    第五章 软件构建中的设计
    第四章 关键的“构建”决策
  • 原文地址:https://www.cnblogs.com/KKKorange/p/8658930.html
Copyright © 2020-2023  润新知