【题解 && 容斥原理】光与镜

题目描述:

【题解 && 容斥原理】光与镜_第1张图片

Solution

首先,这道题看到的第一眼,就能发现这道题是一道容斥。

首先我们套容斥的板子:
a n s = a 1 ∗ S 1 − a 2 ∗ ∣ S 1 ∩ S 2 ∣ + a 3 ∗ ∣ S 1 ∩ S 2 ∩ S 3 ∣ − … … ans = a_1*S_1-a_2*|S_1∩S_2|+a_3*|S_1∩S_2∩S_3|-…… ans=a1S1a2S1S2+a3S1S2S3

其中 S i S_i Si表示数列中 i i i个数的最小公倍数,由于题目中的m特别小,所以我们可以强行枚举。
主要的问题就是系数a的处理。

根据不断的打表以及找规律,我们发现 a i = 2 i − 1 a_i=2^{i-1} ai=2i1

需要注意的两个细节是:
1、一旦遇到数的最小公倍数超过n,那就直接跳出。
2、答案需要开 u n s i g n e d    l o n g    l o n g unsigned\ \ long\ \ long unsigned  long  long


Code

#include
using namespace std;

#define int unsigned long long

int n,m,xs;
bool vi[1010];
int num = 0;
int ans;
int a[1010];

int gcd(int x,int y){
	return y == 0 ? x:gcd(y,x%y);
}

int quickm(int x,int y){
	int sum = 1;
	while (y){
		if (y&1) sum = sum * x;
		x*=x;
		y>>=1;
	}
	return sum;
}

void work(int x){
	int lcm = 1;
	for (int i = 1; i <= m; i++){
		if (!vi[i]) continue;
		if (lcm / gcd(lcm,a[i]) > n/a[i]) return;
		lcm = lcm / gcd(lcm,a[i]) * a[i];
	}
	ans+=xs * (quickm(2,x-1) * (n/lcm));
}

void dfs(int x,int y){
	if (num == y){work(y);return;}
	if (x > m) return;
	vi[x] = 1;num++;
	dfs(x+1,y);
	vi[x] = 0;num--;
	dfs(x+1,y);
}

signed main(){
	freopen("light.in","r",stdin);
	freopen("light.out","w",stdout);
	scanf("%llu %llu",&n,&m);
	for (int i = 1; i <= m; i++) scanf("%llu",&a[i]);
	for (int k = 1; k <= m; k++){
		xs = k%2?1:-1;
		num = 0;dfs(1,k);
	}
	printf("%llu",ans);
	return 0;
}

你可能感兴趣的:(数学,题解)