1. 程式人生 > >「NOIP2018模擬賽」 計數

「NOIP2018模擬賽」 計數

題目描述

給出 m(<=20) 個數 a[1],a[2],…,a[m](<=1e9) 求 1~n(<=1e9) 中有多少數不是 a[1],a[2],…,a[m]的倍數。

分析

比較裸的容斥。考慮在1~n中有多少是a[1],...,a[m]的倍數。設是a[i]的倍數的集合為Ai,則\left|A_i|\right=\left \lfloor \dfrac{n}{a[i]} \right \rfloor,然後用容斥原理求\left|\bigcup_{i=1}^mA_i\right|,最後用總個數n減去就是1~n中不是a[1],...,a[m]的倍數的數的個數了。求的過程用Dfs實現,複雜度為O(2^m),對於m<=20的資料來說足夠了。

程式碼

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
long long n,m,depth;
long long a[25],d,ans;
int flag[25];
long long gcd(long long x,long long y) {
	return y==0?x:gcd(y,x%y);
}
long long lcm(long long x,long long y) {
	return x/gcd(x,y)*y;
}
void dfs(int dep,long long s,int last) {
	if (s>n) return;
	if (dep==depth+1) {
		d+=(n/s);
		return;
	}
	for (int i=last;i<=m;i++) {
		if (!flag[i]) {
			flag[i]=1;
			dfs(dep+1,lcm(s,a[i]),i+1);
			flag[i]=0;
		}
	}
}
int main() {
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++) scanf("%lld",&a[i]);
	for (int i=1;i<=m;i++) {
		memset(flag,0,sizeof(flag));
		depth=i;
		d=0;
		dfs(1,1,1);
		if (i&1) ans+=d;
		else ans-=d;
	}
	printf("%lld",n-ans);
	return 0;
}