zoukankan      html  css  js  c++  java
  • BZOJ 2301

    题意:对于给出的n个询问,每次求有多少个数对(x,y),满足axbcyd,且gcd(x,y)=k
    首先用容斥原理将一个询问拆成4个。然后,一种可行的转化是求一种可行的转化是先令k=1,即求1floor(x/k)n1floor(y/k)m 的互质数对(x,y)的数量。然后我们考虑用莫比乌斯反演。我们经常使用的一种形式是

    f(k)=k|dμ(dk)F(d)


    因此,我们可以设f(k)为范围内gcd(x,y)=k的数对个数(即一个询问的答案),然后设F(d)为范围内d|gcd(x,y)的数对个数。我们会发现F[d]是比较好算的:它就是ndmd,其中nm分别是xy的取值范围。别忘了之前我们已经令k=1,所以我们枚举的d其实就是正整数。如果我们枚举d的话,在原本k=1的时候,每次询问的时间复杂度就会退化成O(n)的,所以我们需要改进。
    在1257那道题里面,我们曾经利用过一个结论:n/d的值有O(n)个。这样,我们就可以枚举这个商,以每个商为一块,分块计算之。这里,我们继续利用这个结论。我们设d在区间[l,r]上时,F[d]=ndmd的值不变。为了保险起见,我们把它拆成两个式子:

    nl=nrml=mr

    然后就是和1257一样的方法,用整除的性质放缩一下,分别得出r关于l的两个范围,取较小的那一个就行了。下一次循环的l就是r+1

    // BZOJ 2301
    
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
     const int N=50000+5;
    
     #define rep(i,a,b) for (int i=a; i<=b; i++)
     #define dep(i,a,b) for (int i=a; i>=b; i--)
     #define read(x) scanf("%d", &x)
     #define fill(a,x) memset(a, x, sizeof(a))
    
     /* 筛Mobius函数的过程:
     思想一:一边计算miu函数,一边筛质数;
     思想二:每个数只被其最小的质数筛去
     */
     bool vis[N];
     int miu[N], T, a, b, c, d, k, prime[N], sum[N], s1, s2, s3, s4;
     void get_Mobius() {
        int tot=0;
        miu[1]=1; 
        fill(vis, false);
        rep(i,2,N) {
            if (!vis[i]) { prime[++tot]=i; vis[i]=true; miu[i]=-1; }
            for (int j=1; j<=tot && prime[j]*i<=N; j++) {
                int k=prime[j]*i;
                vis[k]=true;
                if (i%prime[j]==0) { miu[k]=0; break; }
                else miu[k]=-miu[i]; 
            }
        }
        sum[0]=0;
        rep(i,1,N) sum[i]=sum[i-1]+miu[i];
     }
    
     int query(int n, int m) {
        int last=0, ret=0;
        if (n>m) swap(n, m);
        for (int i=1; i<=n; i=last+1) {
            last=min(n/(n/i), m/(m/i));
            ret+=(n/i)*(m/i)*(sum[last]-sum[i-1]);
        }
        return ret;
     }
    
    int main()
    {
        get_Mobius();
        read(T);
        while (T--) {
            read(a); read(b); read(c); read(d); read(k);
            a--; c--;
            int s1=query(b/k, d/k), s4=query(a/k, c/k);
            int s2=query(a/k, d/k), s3=query(c/k, b/k);
            printf("%d
    ", s1-s2-s3+s4);
        }
    
        return 0;
    }
    
  • 相关阅读:
    指针和引用作为函数参数传递
    cv::Mat.type()
    Matlab 双目标定工具箱
    invalid conversion from `const void*' to `void*'
    error: 'vector' is not a member of cv
    单例模式与静态成员
    RGBD SLAM V2 +Ubuntu 16.04+ROS KINETIC配置及运行
    EntityFrameworkCore + MySQL 主从复制应用读写分离
    Docker 搭建 MySQL8.0 主从复制环境
    Asp.Net Core 项目中使用 Serilog 输出日志到 Elasticsearch
  • 原文地址:https://www.cnblogs.com/yearwhk/p/5119859.html
Copyright © 2011-2022 走看看