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

题意:给定数列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 1000000007
using 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 1000000007
using 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;
}

     

                                


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值