zoukankan      html  css  js  c++  java
  • SPOJ PGCD

    今天做的一个很有成就感的题目,虽然经过我一个上午的痛苦挣扎,但是我觉得这个时间还是花的挺有意义的。

    题目的意思是给你a和b两个数(范围是10^7),从1-a选一个数x,从1-b中间选择一个数,问你能选出来gcd(a,b)=素数 的方案数有多少?

    这是一类典型的gcd统计问题,也是十分有代表性的一个题目。

    首先看到这个题目的时候我也不知道如何入手呢,任何的想法都逃不过T的阴影。

    后来去网上看了各路神牛的题解我才稍稍明白了过来呢。

    这个题目主要用到的只是就是莫比乌斯反演(Mobius)。

    其本质是容斥原理。

    如果我们假设d是一个质数,那么题目要我们求的就是 Sigama( mobi[j]*(n/(d*j))*(m/(d*j)) );

    这里也就把容斥原理体现得淋漓尽致了,什么意思呢?

    我们可以这样理解,我们枚举所取的两个数的最小公倍数的情况,如果当前我们枚举的数为x,那么在它在第一个范围里面有a/x种选择;它在第二个范围里面有b/x种选择,显然对于当前,我们总共的选择数就有(a/x)*(b/x),然而,我们到底加上这个数还是减去这个数呢?首先如果x是一个质数的话,我们应该加上去,但是如果x是恰好由两个不同质数组成的话呢,我们就要减去这个数(显然一个质数的情况已经把两个质数的种类数加进去了,所以这次要把多余的减出来),这里我们就可以得到一个规律,如果把x分解成质数的连乘形式中不含有任何相同的项,那么如果质数的个数为奇数的话,应该加上这个情况数,如果为偶数的话就应该减去这个数。

    讲到这里,你也许会认为这个题目可以引刃而解了,但是你看看数据你就会知道,,如果赤裸裸的算的话,时间复杂度是(n*log(n)),果断是不能承受的。(本题连(O(n))的时间复杂度都难以承受哦)。

    于是我们不得不再想优化的办法呢。

    刚刚说的是枚举所取得的gcd(质数),不如我们换一个思路想想,其实我们可以直接枚举(j*d),也就是质数的若干倍。

    同上面容斥原理的分析法,我们根据(j*d)中间分解成质数连乘后,可以很迅速的得出前面的容斥系数(也就是要加上多少或者减去多少的那个数)。

    由于j*d里面可以有很多个质数因子组成(设为k个),如果k为偶数,那么d可以是其中任何一个(即d可能有k种取法),这是剩下的因子的个数是奇数个,我们应该加上这个数,所以是正1,由于有k种取法,这是的容斥系数应该是正k;同理当k为奇数时,容斥系数为负k。

    但是,如果j*d里面有恰好一个平方因子呢?这是我们的d只可能取那个平方因子的那个数(想想为什么?不然就为o啦),所以这里的容斥系数质可能为1或者-1哦。

    对于其他的(多对平方因子),直接等于0。

    有了这个想法,我们可以先预处理每个数对应的那个容斥系数,这样就加速啦。

    优化到这里,我们离AC又进了一步。但是用这个算法一步步枚举j*d,然后求和,时间复杂度还是有O(n) ,无法通过。

    我们要继续想点别的办法。

    于是我们又回到求和的那个式子——Sigama( mobi[j]*(n/(d*j))*(m/(d*j)) );

    我们可以看到,对于某些d*j,他们所对应的(n/(d*j))*(m/(d*j))的值是不变的,什么意思呢?

    举个例子:8/3=8/4=2(向下整除),8/5=8/6=8/7=8/8=1;

    于是我们又想,对于那种很大的n和m,他们这种情况会更加明显。

    于是我们可以通过对mobi函数求一个前缀和,存入S数组,进行分块处理。

    于是我们可以把对应值相同的所有d*j一起处理,由于对于一个被除数,它所对应的商不会太多,这样分块处理后就可以把时间复杂度减低到sqrt级别了。

    这样的话就可以顺利的过了这个题目啦。

    注:SPOJ卡常数已经到了无节操的境界,所以任何可以降低常数的方法都应该用上的。

    下面上代码(部分参考):

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdio>
    #define ll long long
    #define maxn 10000001
    using namespace std;
    
    char cnt1[maxn],cnt2[maxn];//把字符当做整形,因为longlong的范围最多都只有64位,足够,而且这里运算快得多,有了这个优化时间直接减少至少15s。
    int s[maxn];
    
    void getprim()
    {
        int k;
        s[1]=0;
        for (int i=2; i<maxn; i++)
        {
            if (cnt1[i]==0)
            {
                for (int j=i; j<maxn; j+=i) cnt1[j]++;
            //不用朴素的筛选素数的方法,直接用平方和立方的情况筛选,省时多了。
    if (i<3165) { k=i*i; for (int j=k; j<maxn; j+=k) cnt2[j]++; } if (i<217) { k=i*i*i; for (int j=k; j<maxn; j+=k) cnt2[j]++; }//这里只要考虑平方和立方,为什么?因为含有的i^2的数目等于2和大于2的容斥系数都是0,我们只要知道那个系数大于1就可以了,不必要知道具体是多少。这里减去了好多白花花的时间。
    } if (cnt2[i]==0) k=(cnt1[i]&1)?cnt1[i]:-cnt1[i]; else if (cnt2[i]==1) k=(cnt1[i]&1)?-1:1; else k=0;//k为第i项的容斥系数值,但是我们只需要前缀和。 s[i]=k+s[i-1]; } } int main() { getprim(); int n,m,cas,next,d1,d2; llans; scanf("%d",&cas); while (cas--) { ans=0; scanf("%d%d",&n,&m); for (lli=2; i<=n && i<=m;) { d1=n/i,d2=m/i; next=n/d1<m/d2?n/d1:m/d2; ans+=(ll)(s[next]-s[i-1])*d1*d2; i=next+1;//分块,跳到下一块的起点。 } printf("%lld ",ans); } return 0; }

     

    如有转载,请注明出处(http://www.cnblogs.com/lochan)
  • 相关阅读:
    User Get 'Access Denied' with Excel Service WebPart
    How To Search and Restore files from Site Collection Recycle Bin
    How To Collect ULS Log from SharePoint Farm
    How To Restart timer service on all servers in farm
    How to Operate SharePoint User Alerts with PowerShell
    How to get Timer Job History
    Synchronization Service Manager
    SharePoint 2007 Full Text Searching PowerShell and CS file content with SharePoint Search
    0x80040E14 Caused by Max Url Length bug
    SharePoint 2007 User Re-created in AD with new SID issue on MySite
  • 原文地址:https://www.cnblogs.com/lochan/p/3329785.html
Copyright © 2011-2022 走看看