• bzoj2301: [HAOI2011]Problem b


    【题意】

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

    【题解】

      这算是一道莫比乌斯反演的入门题吧,花了好多时间才略有体会。。。

      首先对于一个询问,直接在原有范围内求出个数显然有点困难,利用容斥简化。

      设ANS(N,M)为1<=X<=N,1<=Y<=M时GCD(X,Y)=K的对数,

      最后的答案就是ANS(B,D)-ANS(A-1,D)-ANS(B,C-1)+ANS(A-1,C-1)。

      现在问题化为求ANS(N,M)。

      设f(d)为N,M范围内最大公约数刚好为d的数对个数,F(d)为含公约数d的数对个数。

      F(d)=$\sum_{d|i}^{}f(i) $(i<=n)

      我们要求的就是f(d)

      反演一下得到

      f(d)=$\sum_{d|i}^{}F(i)\cdot μ(i/d) (i<=n)$

      f(k)就是我们希望的值。

      暴力求显然会超时啊!!!!!(一开始太天真没仔细算复杂度T掉了)

      然后有个神奇的性质:$\left \lfloor \frac{n}{i} \right \rfloor$的不同值个数不超过$2\sqrt{n}$个,

      于是$\left \lfloor \frac{n}{i} \right \rfloor \left \lfloor \frac{m}{i} \right \rfloor$的不同值个数也不超过$2\sqrt{n}$+$2\sqrt{m}$,(这个可以自己YY一下)

      而且相同的值是连续的一段i所产生的。

      所以我们先预处理出前缀和,求出每一段i的μ值,一段一段的加,次数不超过sqrt(n)次。

      时间复杂度O(T*sqrt(n))

    【代码】

     1 #include <iostream>
     2 #include <cstdio>
     3 #define ll long long
     4 #define N 50010
     5 using namespace std;
     6 int Q,a,b,c,d,k,mu[N],prime[N],sum[N],cnt;
     7 bool flag[N];
     8 int read()
     9 {
    10     int x=0;char ch;int sign=1;
    11     do{ch=getchar();if(ch=='-')sign*=-1;}while (ch<'0' || ch>'9');
    12     do{x=x*10+ch-48;ch=getchar();}while (ch>='0' && ch<='9');
    13     return x*=sign;
    14 }
    15 void Pre()
    16 {
    17     mu[1]=1;
    18     for (int i=2;i<N;++i)
    19     {
    20         if (!flag[i])    prime[++cnt]=i,mu[i]=-1;
    21         for (int j=1;j<=cnt;++j)
    22         {
    23             if (i*prime[j]>=N) break;
    24             flag[i*prime[j]]=1;
    25             if (i%prime[j]==0)    break;
    26             mu[i*prime[j]]=-mu[i];
    27         }
    28     }
    29     for (int i=1;i<N;++i)    sum[i]=sum[i-1]+mu[i];
    30 }
    31 ll solve(int n,int m)
    32 {    
    33     ll ans=0;
    34     n/=k;m/=k;
    35     int l=n<m?n:m,last;
    36     for (int i=1;i<=l;i=last+1)
    37     {
    38         last=min(n/(n/i),m/(m/i));
    39         ans+=(ll)(n/i)*(m/i)*(sum[last]-sum[i-1]);
    40     }
    41     return ans;
    42 }
    43 int main()
    44 {
    45     Pre();
    46     Q=read();
    47     while (Q--)
    48     {
    49         a=read()-1;b=read();c=read()-1;d=read();k=read();
    50         printf("%lld\n",solve(b,d)-solve(a,d)-solve(b,c)+solve(a,c));        
    51     }
    52     return 0;
    53 }
    View Code
  • 相关阅读:
    VS扩展开发 二 从示例程序出发
    VS扩展开发 一 导航
    CLR笔记 二 函数调用
    使用C#调用C++类库
    C#编程常用工具总结
    CLR笔记 一 概述
    C#工程中 使用targets和props实例
    VS C++工程的静态库引用问题
    C#高级编程笔记(三)
    (转)如何让CPU的使用率一直在50%
  • 原文地址:https://www.cnblogs.com/Bleacher/p/6770071.html
Copyright © 2020-2023  润新知