CF803F Coprime Subsequences

题面

一句话题意:给你一个序列,问你有多少个子序列的gcd=1

题解:
我们设\(f(x)\)表示\(gcd\)\(x\)的子序列数。显然,我们要求\(f(1)\)
因为是求子序列数,所以我们可以不用管数字的顺序。
观察\(x>1\)的情况,此时\(gcd\)不为1,也就是说,这些满足条件的
子序列里的所有数都是\(x\)的倍数。
\(t[i]\)表示序列中\(i\)的倍数的出现次数,这个可以\(n\sqrt{n}\)求出。
但是这样\(f(x)\)还是不好求。
那什么好求呢?发现如果子序列里的数都是x的倍数,就比较好求。
设g(x)表示序列中只有x的倍数组成的合法子序列个数。这个可以直接O(1)求,

\[g(x)=2^{t[x]}-1 \]

再回过头来研究f和g的关系:发现:$$g=f*1$$
用一下莫比乌斯反演,就可以得出:$$f(n)= \sum_{n|d} g(d)\mu(\frac{d}{n}) $$
这样,\(f(1)\)就很好求了。筛一下\(\mu\)函数就好了~
复杂度\(O(n\sqrt{n})\)
代码:

#include<bits/stdc++.h>
using namespace std;
#define re register ll
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline ll
template<class D>I read(D &res){
	res=0;register D g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
const ll Mod=1e9+7;
ll n,m,ans,v[101000],twice[101000],a[101000],t[101000],prime[101000],tot,mu[101000];
int main(){
	read(n);
	F(i,1,n)read(a[i]);
	F(i,1,n){
		F(j,1,sqrt(a[i])){
			if(a[i]%j==0){
				t[j]++;
				if(j*j!=a[i])t[a[i]/j]++;
			}
		} 
	}
	tot=0;mu[1]=1;twice[0]=1;
	F(i,1,100000)twice[i]=twice[i-1]*2ll%Mod;
	F(i,2,100000){
		if(!v[i])prime[++tot]=i,mu[i]=-1;
		F(j,1,tot){
			if(i*prime[j]>100000)break;
			v[i*prime[j]]=1;
			if(i%prime[j]==0){
				mu[i*prime[j]]=0;
				break;
			}
			else mu[i*prime[j]]=-mu[i];
		}
	}
	F(i,1,100000)ans=(ans+(ll)(twice[t[i]]-1)*mu[i]+Mod)%Mod;
	printf("%lld",ans);
	return 0;
}
posted @ 2019-12-23 19:42  Purple_wzy  阅读(169)  评论(0编辑  收藏  举报