标签:pen lld ret def pre cstring 描述 max count
给出\(m\)个数\(a[1],a[2],…,a[m]\)
求1~n中有多少数不是\(a[1],a[2],…,a[m]\)的倍数。
对于\(60\%\)的数据\(1<=n<=10^6\)对于另外\(20%\)的数据,\(m=2\)
对于\(100\%\)的数据,\(1<=n<=10^9,0<m<=20,1<=a[i]<=10^9\)
这道题显然是容斥
我们可以统计1~n中有多少数是某个\(a_i\)的倍数
显然1~n中\(x\)的倍数有\(n/x\)个
容斥:加上每个\(a_i\)的倍数个数,减去每个同时是\(a_i\)和\(a_j\)的倍数的数的个数,加上每个同时是\(a_i,a_j,a_k\)的倍数的数的个数……
\[\sum_{1\leq b_1< b_2 ...< b_k\leq n}(-1)^{k-1}\frac{n}{lcm(a_{b_1},a_{b_2}...a_{b_k})}\]
\(2^m\)枚举二进制表示是哪些数的倍数,求lcm即可
一开始求lcm直接暴力,跑得有些慢,求lcm的复杂度较大
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m,a[25],maxx;
long long ans;
int gcd(int x,int y){
if(!y) return x;
return gcd(y,x%y);
}
int lcm(int x,int y){
if(x<y) swap(x,y);
return x*y/gcd(x,y);
}
long long solve(int x){
int t=1,f=-1;
for(int i=0;i<m;i++)
if((x>>i)&1){
t=lcm(t,a[i+1]);
f=-f;
}
return n/t*f;
}
int main()
{
// freopen("count.in","r",stdin);
// freopen("count.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d",&a[i]);
maxx=(1<<m)-1;
for(int i=1;i<=maxx;i++)
ans+=solve(i);
printf("%d\n",n-ans);
// fclose(stdin); fclose(stdout);
return 0;
}
我们发现,求\(lcm(a_{b_1},a_{b_2},...,a_{b_k})\)时
我们一定求过\(lcm(a_{b_1},a_{b_2},...,a_{b_{k-1}})\)了
我们可以记录下每次求得的lcm值,求k个数的lcm可以用k个数里面的k-1个数的lcm直接求出来
复杂度\(O(2^m(m+\log a_i))\)
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
LL n,m,a[25],maxx,ans;
bool mark[1000010];
LL lcm[1048580],f[1048580];
LL gcd(LL x,LL y){
if(!y) return x;
return gcd(y,x%y);
}
LL Lcm(LL x,LL y){
if(x<y) swap(x,y);
return x*y/gcd(x,y);
}
LL solve(LL x){
for(LL i=0;i<m;i++)
if((x>>i)&1){
lcm[x]=Lcm(a[i+1],lcm[x-(1<<i)]);
f[x]=-f[x-(1<<i)];
break;
}
return f[x]*n/lcm[x];
}
int main()
{
// freopen("count.in","r",stdin);
// freopen("count.out","w",stdout);
scanf("%lld%lld",&n,&m);
if(n<=1000000){
int x;
for(int i=1;i<=m;i++){
scanf("%d",&x);
for(int j=x;j<=n;j+=x)
mark[j]=1;
}
for(int i=1;i<=n;i++)
if(!mark[i]) ans++;
printf("%lld\n",ans);
fclose(stdin); fclose(stdout);
return 0;
}
for(int i=1;i<=m;i++)
scanf("%lld",&a[i]);
maxx=(1<<m)-1;
lcm[0]=1; f[0]=-1;
for(LL i=1;i<=maxx;i++)
ans+=solve(i);
printf("%lld\n",n-ans);
// fclose(stdin); fclose(stdout);
return 0;
}
一个区间的价值定义为该区间中的最大值减最小值
给定\(n\)个数,求所有区间价值中,第\(k\)大值为多少。
标签:pen lld ret def pre cstring 描述 max count
原文地址:https://www.cnblogs.com/yjkhhh/p/9593134.html