关键词:求前s个数中k个gcd为1的数的组数
题意:求k个数a1,a2…ak,使得gcd(a1,a2…ak)>1的数的组数,其中1=< ai<=s。
解法:容斥原理。
法一:
1.设gcd(a1,a2…ak)=d,按d的整除性划分。
Ap
:当d能被素数p整除时,(a1,a2…ak)的组数。ans=
|Ap1∪Ap2∪...∪Apm|
。
2.
|Ap1∩Ap2∩..∩Apn|
=
Cks/(p1∗p2∗...∗pn)
,记为B(s,k,p1*p2*…*pn)
所以可以用容斥原理求解。
同时,也可以用mobius函数的形式简化容斥原理形式
ans=
∑si=1mu[i]∗B(s,k,i)
复杂度:O(s),即O(n)
拓展:
1.求a[1,2…n]中k个gcd>1的数的个数
思路与上述解法相同,只是在计算分量时,稍加变化
|Ap1∩Ap2∩..∩Apn|
=
Ckbeinum[p1∗p2∗...∗pn]
beinum[i]:a[1,2…n]中是i的倍数的个数。通过O(n*
max(a[i])−−−−−−−−√
)预处理可以得到
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#define lowbit(x) x&(-x)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
#define rad 10000
const ll maxn =50+10;
ll k,s,ans;
ll c[maxn][maxn],mu[maxn];
bool not_prime[maxn];
vector<ll> prime;
ll C(ll n,ll m){
if(n<m) return c[n][m]=0;
if(c[n][m]) return c[n][m];
if((m==0)||(m==n)||(n==1)) return c[n][m]=1;
return c[n][m]=C(n-1,m-1)+C(n-1,m);
}
void mobius(){
mu[1]=1;
for(ll i=2;i<maxn;i++){
if(!not_prime[i]) prime.push_back(i),mu[i]=-1;
for(ll j=0;j<prime.size(),i*prime[j]<maxn;j++){
not_prime[i*prime[j]]=1;
if(!(i%prime[j])) { mu[i*prime[j]]=0;break; }
else { mu[i*prime[j]]=-mu[i]; }
}
}
}
int main(){
memset(c,0,sizeof(c));
mobius();
//freopen("a.txt","r",stdin);
while(scanf("%lld%lld",&k,&s)!=EOF){
ans=0;
for(int i=2;i<=s;i++){
ans-=mu[i]*C(s/i,k);
}
if(ans>10000) printf("10000\n");
else printf("%lld\n",ans);
}
return 0;
}
法二:
法一的缺陷在于只能求gcd为1的集合个数,法二则能求gcd为i的集合个数!
f[i]:表示n个数中选取k个数gcd为i的集合个数
f[i]=C(beinum[i],k)-
∑j为i的约数f[j]
复杂度:O(n*sqrt(n))
#include <iostream>
#include <cstdio>
#include <set>
#include<string.h>
#include <cstdlib>
using namespace std;
typedef long long ll;
# define N 100010
ll k,s,cnt[100],f[100],ans;
ll c[100][100];
ll C(ll a,ll b){
if(a<b) return c[a][b]=0;
if(c[a][b]) return c[a][b];
if(a==b||b==0||a==1) return c[a][b]=1;
return c[a][b]=C(a-1,b)+C(a-1,b-1);
}
int main(){
scanf("%lld%lld",&k,&s);
memset(cnt,0,sizeof(cnt));
memset(c,0,sizeof(c));
ans=0;
for(ll i=1;i<=s;i++) cnt[i]=s/i;
for(ll i=1;i<=s;i++) f[i]=C(cnt[i],k);
for(ll i=s;i>=1;i--){
for(ll j=1;j*j<=i;j++){
if(i%j==0){
if(j!=i) f[j]-=f[i];
if(i!=j*j&&(i/j)!=i) f[i/j]-=f[i];
}
}
}
for(ll i=2;i<=s;i++) ans+=f[i];
if(ans<=10000) printf("%lld\n",ans);
else printf("10000\n");
return 0;
}
拓展2:求n个数a[1],a[2]…a[n]中gcd为k的集合个数
f[i]=
(2beinum[i]−1)
-
∑j为i的约数f[j]
复杂度:O(n*sqrt(n))