容斥原理 & Mobious函数

容斥原理

此处为笔记图片

例题:Devu和鲜花

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<iomanip>
#include<cstring>
#include<string>
#define LL long long

using namespace std;
const LL mod = 1e9 + 7;
LL n, m, a[25], ans;
LL dwn = 1;

long long read(){
	long long x=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*h;
}

LL power (LL a, LL b) {
	LL sum = 1;
	for ( ; b > 0; b >>= 1) {
		if (b & 1)sum = sum * a % mod;
		a = a * a % mod;
	}
	return sum;
}

LL inv (LL a) {
	return power(a, mod - 2) % mod;
}

LL C (LL N, LL M) {  // 注意数据类型!不是int
	if (N > M) return 0;
	if (M < 0) return 0;
	LL s = 1;
	for (LL i = M; i >= M - N + 1; i --) s *= (i % mod), /*处处取模好习惯!*/ s %= mod;
//	cout << "-" << s << endl;
	s = s * dwn % mod;
	return s % mod;
}

int main() {
	n = read(); m = read();
	for (int i = 1; i <= n; i ++) a[i] = read();
	for (int i = 1; i <= n - 1 ; i ++) dwn *= inv(i), dwn %= mod;  // C(n-1,*)中n-1不变,所以可以预处理,缩短时间
	
	for (LL i = 0; i <= (1 << n) - 1; i ++) {
		LL fh = 1, sum = 0, k = i;
		for (LL j = 1; k > 0; k >>= 1, j ++) {
			if (k & 1) {
				fh *= -1;
				sum += (a[j] + 1);
			}
		}
//		cout << sum << endl;
		ans += (C(n - 1, m - sum + n - 1) * fh + mod) % mod;  // 同下!
	}
	cout << ( ans + mod ) % mod << endl;  // 注意!此处需要防止ans是负的
	return 0;
}

莫比乌斯函数

笔记:[此处为图片]

例题:ZAP-Queries

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<iomanip>

using namespace std;
long long T,a,b,d,ans;
long long mb[50010];
long long pri[50010],idx=0;
bool ss[50010];

long long read(){
	long long x=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*h;
}

inline long long mymin(long long a,long long b){
	return (a>b ? b:a);
}
/*
这是第一种莫比乌斯函数的求法,运用的是线性筛
*/
/*
void init(){
	for(long long i=1;i<=50000;i++)mb[i]=1,ss[i]=0;  // 提前预处理
	
	for(long long i=2;i<=50000;i++){  // 细节:不能sqrt(50000)
		if(!ss[i]){
			mb[i]=-1;
			for(long long j=2;i*j<=50000;j++){
				if(j%i==0)mb[i*j]=0;  // 因为我更新的是i*j,如果j%i==0,那么,i*j%(i^2)一定0,mb[i*j]=0
				else mb[i*j]*=-1;
				ss[i*j]=1;
			}
		}
	}
	
	for(long long i=1;i<=50000;i++) mb[i]+=mb[i-1];
	return ;
}
*/

/*
这是运用埃氏筛的求法,速度更快,细节更多
*/
void init(){
	mb[1]=1;  // 细节:如果写成线性筛那样,那你人没了
	
	for(long long i=2;i<=50000;i++){
		if(!ss[i]){
			mb[i]=-1;
			pri[++idx]=i;
		}
		for(long long j=1;j<=idx&&pri[j]*i<=50000;j++){
			ss[i*pri[j]]=1;
			if(i%pri[j]==0)break;
			else mb[i*pri[j]]=-mb[i];  // 细节:这里一定要注意理解,只有所有质因子幂次数为1的数,才会进行else运算
                                                   // 例如 18 :它是i=9时被标记的,虽然9%2=1,但是mb[9]本身为0,所以进行了也没个卵用
                                                   // 9会在i=3时标记,因为3%3=0,所以mb[9]=0
                                                   // 因为那些质因子幂数大于一的数我不会对其的mb值更新,
                                                   // 所以一开始只能写个mb[1]=1;,而不能写成线性筛那样
		}
	}
	
	for(long long i=1;i<=50000;i++) mb[i]+=mb[i-1];
	return ;
}

int main(){
	init();
	scanf("%lld",&T);
	while(T--){
		ans=0;
		scanf("%lld %lld %lld",&a,&b,&d);
		a=a/d; b=b/d;
		long long MIN=mymin(a,b);
		for(long long i=1;i<=MIN; ){
			long long nxt=mymin(a/(a/i),b/(b/i));    // 节约时间,g(x)=floor(a/floor(a/x)),但跳的时候一定要保证floor(a/i)*floor(b/i)一样
                                                                 // 所以要取Min
			nxt=mymin(MIN,nxt);
			ans+=(mb[nxt]-mb[i-1])*((a/i)*(b/i));
			i=nxt+1;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

posted @ 2021-11-01 23:25  Charisk_FOD  阅读(100)  评论(0)    收藏  举报