HDU-6053 TrickGCD 前缀和数组,莫比乌斯函数

来源:互联网 发布:尘埃落定 知乎 编辑:程序博客网 时间:2024/05/19 13:56

题意:给定数列A,问有多少个数列B满足一下条件
          1.1<=Bi<=Ai

          2.对于任意  1<=l<=r<=len(A)  , 有gcd(Bl,Bl+1,……Br)>=2

          全是就是gcd(B)>=2

思路:

实在太弱了,多校中写不出来,就想着找最小的Ai,然后遍历每个gcd遍历一遍数组A,  1E10的复杂度 会爆

看了大佬们的博客,

有些还用了莫比乌斯反演

有些没用直接求,再容斥原理去重

一直在想为什么将数列中的数字转化为权值数组,即存值小于X的数有多少个可以把  n^2 降到  nlogn

因为  j*i-1~j*i+i-1的数值除i得到的结果都是相同的
枚举 gcd后   复杂度一共为  n/1  +  n/2  +  n/3  +  ...........=nlogn

容斥这一块
不用莫比乌斯函数的话,普通的容斥原理,ans[a]-=(ans[2a]+ans[3a]+ans[4a]+......),得到的答案才是gcd=a对答案的贡献
同样ans[2a]-=(ans[4a]+ans[6a]+ans[8a]+......)
显然要先得到gcd=2a的ans[2a]才能得到 gcd=a的ans[a],因此要从后往前遍历容斥


#include<bits/stdc++.h>#define N 1000000007using namespace std;long long f(long long a,long long b){long long ans=1;while(b){if(b&1) ans=(ans*a)%N;a=(a*a)%N;b>>=1;}return ans; } int num[100009];long long pp[100009];long long ans[100009];int main(){int k=1;int t,len,a;scanf("%d",&t);while(t--){scanf("%d",&len);memset(num,0,sizeof(num));for(int i=0;i<len;i++){scanf("%d",&a);num[a]++;}for(int i=1;i<=100000;i++)num[i]+=num[i-1];bool flag=false;for(int i=2;i<=100000;i++)//枚举gcd  if(flag) ans[i]=0;else{if(num[i-1]>0){flag=true;ans[i]=0;continue;}ans[i]=1;for(int j=1;j*i<=100000;j++){ans[i]=ans[i]*(f(j,num[min((j+1)*i-1,100000)] - num[j*i-1]))%N;}}long long p=0;for(int i=100000;i>=2;i--){for(int j=2*i;j<=100000;j+=i){ans[i]-=ans[j];ans[i]=(ans[i]%N+N)%N;} p = (p+ans[i])%N;  }printf("Case #%d: %lld\n",k++,p);}return 0;}

莫比乌斯函数

这里分享两个博客感觉挺不错的

莫比乌斯函数

读贾志鹏线性筛有感 (莫比乌斯函数的应用)

根据容斥原理,我们知道,ans = +[k=一个不同素数之积 时对答案的贡献]
                                                  =  -[k=两个不同素数之积 时对答案的贡献]
                                                  = +[k=三个不同素数之积 时对答案的贡献]

比如上面那个图,ans=1*F[2]+1*F[3]+1*F[5]+(-1)*F[6]+(-1)*F[10]+(-1)*F[15]+ 1*F[30]+。。。。。。


刚好是莫比乌斯函数的相反数

 感觉就像一位大佬说的那样 (-1)^K   ,n=p1p2p3……pk  这实际上就是在容斥

 

#include<bits/stdc++.h> using namespace std;#define N 1000000007using namespace std;long long f(long long a,long long b){long long ans=1;while(b){if(b&1) ans=(ans*a)%N;a=(a*a)%N;b>>=1;}return ans; } int mu[100005];int num[100005];bool book[100005];int prime[100005]; void init()  //莫比乌斯函数表 {    memset(book,false,sizeof(book));    mu[1] = 1;    int cnt = 0;    for(int i = 2;i <= 100000;i++)    {        if(!book[i])                //第一个遇见的素数         {            prime[cnt++] = i;       //素数表             mu[i] = -1;        }        for(int j = 0;j < cnt && i*prime[j] <=100000;j++)        {            book[i*prime[j]] = true;            if(i % prime[j]) mu[i*prime[j]] = -mu[i];            else             {                mu[i*prime[j]] = 0;                break;            }        }    }}int main(){int k=1;init();int t,len,a,Min;scanf("%d",&t);    long long ans,temp;    while(t--){Min=100000;scanf("%d",&len);memset(num,0,sizeof(num));for(int i=0;i<len;i++){scanf("%d",&a);num[a]++;Min=min(Min,a);}for(int i=1;i<=100000;i++)num[i]+=num[i-1];ans=0;for(int i=2;i<=Min;i++){if(!mu[i]) continue;temp=1;for(int j=1;j*i<=100000;j++)temp=temp*(f(j,num[min((j+1)*i-1,100000)] - num[j*i-1]))%N;ans=ans-mu[i]*temp;   //莫比乌斯函数相反数 ans=(ans%N+N)%N;}printf("Case #%d: %lld\n",k++,ans);}return 0;}

     

                                


原创粉丝点击