zoukankan      html  css  js  c++  java
  • NOI 2016 循环之美 (莫比乌斯反演+杜教筛)

    题目大意:略 洛谷传送门 鉴于洛谷最近总崩,附上良心LOJ链接

    任何形容词也不够赞美这一道神题

    $sumlimits_{i=1}^{N}sumlimits_{j=1}^{M}[gcd(i,j)==1][gcd(j,K)==1]$

    $sumlimits_{j=1}^{M}[gcd(j,K)==1]sumlimits_{i=1}^{N}[gcd(i,j)==1]$

    我们先处理右边的式子$sumlimits_{i=1}^{N}[gcd(i,j)==1]$:

    $sumlimits_{i=1}^{N}sumlimits_{d|gcd(i,j)}mu(d)sumlimits_{j=1}^{M}[gcd(j,K)==1][d|j]$

    $sumlimits_{d=1}^{N}mu(d)left lfloor frac{N}{d} ight floor sumlimits_{j=1}^{M}[gcd(j,K)==1][d|j]$

    $sumlimits_{d=1}^{N}mu(d)left lfloor frac{N}{d} ight floor sumlimits_{j=1}^{left lfloor frac{M}{d} ight floor}[gcd(jd,K)==1]$

    加下来就是比较关键的一个展开式子,[gcd(jd,K)==1]是[gcd(d,K)==1]&[gcd(j,K)==1]的充分必要条件:

    $sumlimits_{d=1}^{N}[gcd(d,K)==1]mu(d)left lfloor frac{N}{d} ight floor sumlimits_{j=1}^{left lfloor frac{M}{d} ight floor}[gcd(j,K)==1]$


    84分算法:

    令$f(n)=sumlimits_{i=1}^{n}[gcd(i,K)==1]$

    这是啥?

    欧拉函数$varphi$啊!别和我一样反演傻了连欧拉函数的定义都忘了

    $f(n)=left lfloor frac{n}{K} ight floor varphi(K) + sumlimits_{i=1}^{n;mod;K}[gcd(i,K)==1]$

    预处理出$varphi(K)$和$sumlimits_{i=1}^{n}[gcd(i,K)==1]$,那么$f(n)$就能在$O(1)$求得

    可$sumlimits_{i=1}^{n}[gcd(i,K)==1]mu(d)$就不能用$varphi$了,但经过计算,$g$数组能在大约$O(n*K的质因子个数)$的时间内处理出来,极限情况也只不超过$2.6 cdot 10^{7}$

    那么这个问题就在$O(n)$的时间内被解决了

    蒟蒻的代码写得非常迷就不放了

    100分算法:

    这是一道神仙题

    先放上原式:$sumlimits_{d=1}^{N}[gcd(d,K)==1]mu(d)left lfloor frac{N}{d} ight floor sumlimits_{j=1}^{left lfloor frac{M}{d} ight floor}[gcd(j,K)==1]$

    $sumlimits_{j=1}^{left lfloor frac{M}{d} ight floor}[gcd(j,K)==1]$可以用$84$分算法里的方法预处理,每次$O(1)$得到

    现在令$g(N,K)=sumlimits_{i=1}^{N}[gcd(i,K)==1]mu(i)$

    $=sumlimits_{i=1}^{N}mu(i)*sumlimits_{d|i}mu(d)$

    $=sumlimits_{d|K}mu(d) sumlimits_{i=1}^{N} [d|i]mu(i)$

    $=sumlimits_{d|K}mu(d) sumlimits_{i=1}^{left lfloor frac{N}{d} ight floor} mu(id)$

    关键的部分来了

    如果两个数$gcd(x,y)==1$,说明它们没有公共因子,满足积性函数的性质,那么$mu(xy)=mu(x)mu(y)$

    反之它们存在公共因子,那么$mu(xy)$一定等于$0$

    利用这个性质

    $=sumlimits_{d|K}mu(d)^{2} sumlimits_{i=1}^{left lfloor frac{N}{d} ight floor} [gcd(i,d)==1]mu(i)$

    诶!右面这个东西$sumlimits_{i=1}^{left lfloor frac{N}{d} ight floor} [gcd(i,d)==1]mu(i)$,似乎就是$g(left lfloor frac{N}{d} ight floor,d)$啊!

    递归求解即可

    当$n==0$是,答案就是$0$

    发现$K==1$时不能继续递归了否则会死循环,此时

    $G(n,1)=sumlimits_{i=1}^{n} [gcd(i,1)==1]mu(i)=sumlimits_{i=1}^{n} mu(i)$

    $n$可能很大,上杜教筛即可

     1 #include <map>
     2 #include <cmath>
     3 #include <vector>
     4 #include <cstdio>
     5 #include <cstring>
     6 #include <algorithm>
     7 #define N1 20000010
     8 #define M1 2000010
     9 #define K1 2010
    10 #define ll long long
    11 #define dd double
    12 #define cll const long long 
    13 #define it map<int,int>::iterator
    14 using namespace std;
    15 
    16 int N,M,K,maxn;
    17 int mu[N1],smu[N1],pr[M1],cnt,phik; bool use[N1];
    18 int f[K1],ps[K1],son[K1],is[K1],pn,sn;
    19 vector<int>ss[K1];
    20 void Pre()
    21 {
    22     int i,j,x; mu[1]=smu[1]=1;
    23     for(i=2;i<=maxn;i++)
    24     {
    25         if(!use[i]){ pr[++cnt]=i; mu[i]=-1;}
    26         for(j=1;j<=cnt&&i*pr[j]<=maxn;j++)
    27         {
    28             use[i*pr[j]]=1;
    29             if(i%pr[j]){ mu[i*pr[j]]=-mu[i];}
    30             else{ break; }
    31         }
    32         smu[i]=smu[i-1]+mu[i];
    33     }
    34     for(son[++sn]=1,x=K,phik=K,i=2;i<=K;i++)
    35     {
    36         if(x%i==0){ ps[++pn]=i; phik=phik/i*(i-1); while(x%i==0) x/=i; }
    37         if(K%i==0){ son[++sn]=i; }
    38     }
    39     for(j=1;j<=pn;j++) 
    40         for(i=ps[j];i<=K;i+=ps[j]) is[i]=1;
    41     for(i=1;i<=K;i++) f[i]=f[i-1]+(is[i]?0:1);
    42     for(i=1;i<=sn;i++)
    43     {
    44         x=son[i];
    45         for(j=1;j<=x;j++)
    46             if(x%j==0&&mu[j]) ss[x].push_back(j);
    47     }
    48 }
    49 map<int,int>mp;
    50 ll Smu(int n)
    51 {
    52     if(n<=maxn) return smu[n];
    53     it k=mp.find(n);
    54     if(k!=mp.end()) return k->second;
    55     int i,la;ll ans=1;
    56     for(i=2;i<=n;i=la+1)
    57     {
    58         la=n/(n/i);
    59         ans-=1ll*Smu(n/i)*(la-i+1);
    60     }
    61     mp[n]=ans;
    62     return ans;
    63 }
    64 ll F(int x){return 1ll*(x/K)*phik+f[x%K];}
    65 ll G(int n,int k)
    66 {
    67     if(!n) return 0;
    68     if(k==1) 
    69     {
    70         if(n<=maxn) return smu[n];
    71         else return Smu(n);
    72     }
    73     int i,d;ll ans=0;
    74     for(i=0;i<ss[k].size();i++)
    75     {
    76         d=ss[k][i];
    77         ans+=1ll*G(n/d,d);
    78     }
    79     return ans;
    80 }
    81 
    82 int main()
    83 {
    84     scanf("%d%d%d",&N,&M,&K);
    85     int i,j,la; ll ans=0; maxn=min(max(N,M),20000000); Pre();
    86     for(i=1;i<=min(N,M);i=la+1)
    87     {
    88         la=min(N/(N/i),M/(M/i));
    89         ans+=1ll*(G(la,K)-G(i-1,K))*(N/i)*F(M/i);
    90     }
    91     printf("%lld
    ",ans);
    92     return 0;
    93 }
  • 相关阅读:
    Nodejs-内置核心模块&npm包管理工具

    python 内置函数
    序列化和反序列化(json 和pickle)dumps 为序列化, json为反序列化
    迭代器iter()
    生成器 yield
    装饰器的解释说明
    面向过程中的局部变量(global)
    函数的参数设定
    集合的基本操作
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10241081.html
Copyright © 2011-2022 走看看