HDU Lucky7(容斥+中国剩余定理 + 快速加 + 二进制压位)

给你一个区间,问你在这个区间内 不满足模m[i]等于a[i]的 并且能被7整除的个数有几个

我们先求一下在这个区间里的 7 的倍数有多少个,

然后在求 是 7 的倍数,然后又满足 模 m[i] 等于 a[i]  的有多少个,

由于 m[i],a[i] 有 n 个,但是 n 又不超过 15 个,所以我么可以用充斥原理来求,

奇数就 -  偶数 就 + 。

把  m[i],a[i] 的情况用二进制枚举一下,用中国剩余定理求他们的最小的公共数是多少。然后 

再求 区间里一共有多少个。

主要熟悉一下中国剩余定理求解线性同余方程。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mem(x,v) memset(x,v,sizeof(x))
using namespace std;
typedef long long LL;
LL m[20],a[20];
LL s[20];
LL n,x,y;
void exgcd(LL a, LL b, LL &x,LL &y){
	if (b == 0){
		x = 1; y = 0; return ;
	}
	exgcd(b,a%b,x,y);
	LL tmp = x;
	x = y; y = tmp - a / b * y;
	return ;
}
LL mul(LL a, LL k, LL M){
	LL res = 0;
	while(k){
		if (k & 1) res = (res + a)%M;
		k >>= 1;
		a = (a << 1)%M;
	}
	return res;
}
LL China(){
	LL ans = 0,res,M = 1;
	for (int i = 0; i <= n; i++){
		if (s[i]) M *= m[i];
	}
	for (int i = 0; i <= n; i++){
		if (s[i]){
		    LL x,y;
		    LL Mi = M / m[i];
			exgcd(Mi,m[i],x,y);
			x = (x % m[i] + m[i])% m[i];
			ans = ((ans + mul(a[i]*Mi,x,M)%M)+M)%M;
		}
		
	}
    res = (y + M - ans)/M - (x - 1 + M - ans)/M;//所有符合条件的解为ans = x+k*M,
    //可以知道在[1,y]这个区间内,有(M+y-x)/ M个k符合条件,
    return res;
}

void init(){
	cin>>n>>x>>y;
    for (int i = 0; i < n; i++)
    	scanf("%I64d%I64d",&m[i],&a[i]);
    m[n] = 7; a[n] = 0; s[n] = 1;
    return;
}


int main(){
    int T,num = 1;
    cin>>T;
    while(T--){
    	init();
    	LL ans = 0;
    	for (int i = 0; i < (1 << n); i++){ //二进制压缩。
    		int k = 0;
    		for (int j = 0; j < n; j++){
    			if (i & (1 << j)) s[j] = 1,k++; else s[j] = 0;
    		}
    		k = k & 1 ? -1 : 1; //这里用到了容斥原理。奇数 -,偶数 +。
    		ans += China()*k;	//i 一定从 0 开始,0 是算 7 的倍数的。
    	}
    	printf("Case #%d: ",num++);
    	printf("%I64d\n",ans);
    }


	return 0;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/81414666