一类数论容斥题枚举贡献因子的套路

        这里主要提供这类题莫比乌斯的推导套路,推完柿子再维护东西思路会清晰许多

A hdu4135 
题意:

        给A B N 问【A,B】中与N互质的个数,N<=1e9,B<=1e15

公式推导:

        先将题意化成柿子:

        

        由 ,*指狄利克雷卷积,其实就是莫比乌斯反演啦

        

        令,*是乘法,换元,并改变求和顺序

        

维护思路:

        由于mu的特性,只有d是不同质因子的乘积对答案才有贡献

        比如 2 2*5 2*7*11有贡献,2*2,12无贡献

        贡献因子的定义:定义这种不同质因子乘积叫贡献因子

        贡献因子的求法:预处理N的质因子,然后容斥N的质因子

        求出ans:遍历N的所有贡献因子d

 g++ ACcode 这里mu其实不用预处理

#include<bits/stdc++.h>
#define ll long long
using namespace std;
bool isp[48005];int pri[48005];int mu[48005];
int p=0;
void init(int n)
{
	for(int i=1;i<=n;i++)isp[i]=1;mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(isp[i]==1) pri[++p]=i,mu[i]=-1;
		for(int j=1;j<=p&&pri[j]*i<=n;j++)
		{
			isp[pri[j]*i]=0;
			if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
			else mu[i*pri[j]]=-mu[i];
		}
	}
}
vector<int>fac;
void gfac(int n)
{
	for(int i=1;pri[i]<=n/pri[i];i++)
	{
		if(n%pri[i]==0)fac.push_back(pri[i]);
		while(n%pri[i]==0)n/=pri[i];
	}
	if(n>1)fac.push_back(n);
}
ll gans(ll B)
{
	int up=fac.size();
	ll ans=B;
	for(int i=1;i<(1<<up);i++)
	{
		int cnt=0;int mul=1;
		for(int j=0;j<up;j++)
		{
			if(((1<<j)&i)!=0)	cnt++,mul*=fac[j];
		}
		if(cnt&1)ans-=(B/mul);
		else ans+=(B/mul);
	}
	return ans;
}
int main()
{
	init(45000);//cout<<45000*45000;
	int T;scanf("%d",&T);int kase=0;
	while(T--)
	{
		fac.clear();
		ll A,B;int n;scanf("%lld %lld %d",&A,&B,&n);
		gfac(n);
		printf("Case #%d: ",++kase);
		printf("%lld\n",gans(B)-gans(A-1));
	}
} 

好了,我们刚热完身,现在来一题难一点的

B.hdu5072   莫比乌斯反演+同色三角形套路
题意:

        求n个不同的数(ai<=1e5,n<=1e5)中有多少组三元组(a, b, c)两两不互质或者两两互质

思路:

        首先将题意转换一下,我们考虑平面上有N个点,两两不共线

        一个三元组代表一个三角形ABC

        由题意,AB,AC,BC 必然有(A,B)=(A,C)=(B,C)=1或!=1

        那么相当于问有多少个同色三角形,AB互质将AB染成红色,不互质染成黑色

        同色三角形的求法:C(n,3) - sum 从1到n:(该点与其他x个点互质*该点与其他y个点不互质) 

        现在问题的核心在:对于ai,有多少个aj与ai互质?

        转换成柿子:

柿子推导:

        

        

        

维护方法:         

        

       统计有多少个ai含有贡献因子d即可

        A线性筛预处理1e5以内的mu,

        B利用这个mu用通用筛维护出1e5以内每个数的贡献因子

        C对每个ai暴力遍历贡献因子,维护出num

        D计算答案

    g++ 312ms

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
bool isp[maxn];int pri[maxn];int mu[maxn];
int num[maxn];int p=0;
vector<int>E[maxn];int a[maxn];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;	
} 
void init()
{
	int up=100001;//A线性筛
	for(int i=1;i<=up;i++)isp[i]=1;mu[1]=1;
	for(int i=2;i<=up;i++)
	{
		if(isp[i]==1)mu[i]=-1,pri[++p]=i;
		for(int j=1;j<=p&&pri[j]*i<=up;j++)
		{
			isp[pri[j]*i]=0;
			if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
			else mu[i*pri[j]]=-mu[i];
		}
	}
	for(int i=1;i<=up;i++)//B预处理每个数的mu贡献因子 
	{
		if(mu[i])
			for(int j=i;j<=up;j+=i)
				E[j].push_back(i);
	}
}
int main()
{
	init();int T;scanf("%d",&T);
	while(T--)
	{
		ll n;scanf("%lld",&n);memset(num,0,sizeof(num));
		for(int i=1;i<=n;i++)
		{
			a[i]=read();//scanf("%d",&a[i]);	
			for(int j=0;j<E[a[i]].size();j++) num[E[a[i]][j]]++;//C
		}ll ans=0;
		for(int i=1;i<=n;i++)//D
		{
			int tem=0;if(a[i]==1)continue;
			for(int j=0;j<E[a[i]].size();j++) tem+=mu[E[a[i]][j]]*num[E[a[i]][j]];
			ans+=tem*(n-1-tem);
		}
		printf("%lld\n",n*(n-1)*(n-2)/6-ans/2);//同色三角形套路 
	} 
}

C 17ICPC乌鲁木齐现场赛 K题 :    

https://blog.csdn.net/animalcoder/article/details/79438720
D hdu6102 (待更)

维护方法:          

猜你喜欢

转载自blog.csdn.net/animalcoder/article/details/80320927