容斥原理 ——小白

1.对容斥原理的描述

在计数时,必须注意没有重复,没有遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。
如果被计数的事物有A、B、C三类,那么,A类和B类和C类元素个数总和= A类元素个数+ B类元素个数+C类元素个数—既是A类又是B类的元素个数—既是A类又是C类的元素个数—既是B类又是C类的元素个数+既是A类又是B类而且是C类的元素个数。(A∪B∪C = A+B+C - A∩B - B∩C - C∩A + A∩B∩C)

如果你看不懂的话,咱们来看一个简单的数学问题,来了解容斥的思想!
例如:一次期末考试,某班有15人数学得满分,有12人语文得满分,并且有4人语、数都是满分,那么这个班至少有一门得满分的同学有多少人?
分析:依题意,被计数的事物有语、数得满分两类,“数学得满分”称为“A类元素”,“语文得满分”称为“B类元素”,“语、数都是满分”称为“既是A类又是B类的元素”,“至少有一门得满分的同学”称为“A类和B类元素个数”的总和。为15+12-4=23。
这就是简单的容斥原理。

2 集合的原理公式

一般用来求几个集合的并集,以3个集合举例,我们要先将3个集合的大小算出来,然后加起来,再减去两两集合的交集,最后再加上3个集合的交集。

关于集合的原理公式

在这里插入图片描述
可以进行化简处理,就变成了这样

B作为所有Ai的集合, 这个公式是由 De Moivre (Abraham de Moivre)提出的。

还可以利用韦恩图的原理来进行理解
用韦恩图来表示3个集合A,B,C
在这里插入图片描述
这个韦恩图明显的展示了 关于AUBUC的面积就等于,A,B,C各自面积的和减去在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
的面积,再加上在这里插入图片描述的面积。
在这里插入图片描述
这样不仅可以求3个集合的并集,还可以求n个集合的。

3 容斥原理的应用

当我们了解了一个算法的原理后还要懂得如何运用它,接下来让我们来看一道例题

可见树木
问题描述
有许多树组成一个m*n网格,网格从(1,1)开始。农夫夏洛克站在(0,0)点。他不知道他能看到多少树。
如果两棵树和夏洛克在一条线上,农民夏洛克只能看到离他最近的那棵树。
输入
第一行包含一个整数t,表示测试用例的数量。然后有多个测试用例。对于每个测试用例,有一行包含两个整数m和n(1≤m,n≤100000)
输出
对于每个测试用例的输出,一行表示农民夏洛克可以看到的树的数量。
样本输入
2
1 1
2 3
样本输出
1
5

对于这个问题,咱们可以知道当两棵树与夏洛克在一条线上是什么情况,就比如最简单的例子(1,1)(2,2) 说明后面的点都是最前面能看到点的倍数,能被看到的点都是横纵坐标公约数为1的点,也就是gcd(x,y)=1,如果有一个点(x,y),它的公约数为k,即gcd(x,y)=k,那么,它就被前面的点挡住了。所以问题就变成了求N*M个点中横纵坐标互质的对数。x的范围是[1,N ],y的范围为[1, M ],我们可以从[ 1,N ]进行遍历x,再用这个x求与[ 1, M ]中的数互质的个数,累加起来就是总的能看到树的数目。
所以我们可以先求与x不互质的数的个数,再用x减去这个个数就是互质的个数

与x不互质的数就是[ 1,M ]中n的素因子的倍数。
例如 M=12,X =15。 15的素因子数为2,3,5。
所以
[1 , 12] 中含有2的倍数有:(2,4,6,8,10,12)=n/2=6个
[1 , 12] 中含有2的倍数有:(3,6,9,12)=n/3=4个
[1 , 12] 中含有2的倍数有:(5,10)=n/5=2个
与x不互质的数的个数就是上面3个集合取并集,这样就需要用到容斥思想了
接下来就是上代码了(我会在代码旁边加注释)

#define isc(x) scanf("%lld",&x)
#define ipr(z) printf("%lld\n",z)
#define mem(x,y) memset(x,y,sizeof(x))
#define mod 100000001
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#define ll long long
const int maxt=110000;
using namespace std;
ll n,m,t,cnt,h[maxt],ant[maxt]; 
 //分解质因子 将质因子存在h数组里面 
void zhishu(ll k){
	cnt =0;
	for(ll i=2;i*i<=k;i ++){
		if(k%i==0){
		 while(k%i==0){
		 	k /=i;
		 }
		 h[cnt++] =i;		
		}
	}
	if(k !=1) h[cnt++] =k;	//注意一下这里还要继续
	//将k赋进h数组
} 
//构造增长的队列数组ant 在ant数组里面存质因子数的个数和正负情况 然后直接用m去除ant中的各项再加起来就是与x不互质的数的个数 ,再用m减去这个数,就是与x互质的数的个数
int divi(ll k){
	ll r=0,w=0,t;
	ant[r++] =-1;
	for(ll i=0;i <cnt;i ++){
		t =r;
		for(ll j=0;j <t;j ++){
			ant[r ++] =ant[j]*h[i]*(-1);		
		}
				
	}
	for(ll i=1;i <r;i ++)
	w +=k/ant[i];
	
	return w;
} 
 
int main()
{
	isc(t);
    while(t --){
    ll	sum =0;
    	isc(n); isc(m);
		if(n>m) swap(n,m);
		//取较小的值去遍历,减少运算
		for(ll i=1;i <=n;i ++){
			zhishu(i);//对每个i进行分解质因子
			sum +=(m-divi(m));
		}
    	ipr(sum);
	}
    
    return 0;
}

我们还是可以举上面那个例子x=12,y=15
15的素因子为2,3,5 存在了h数组里面,然后ant数组里面存的是-1,2,3,-6
12/2 =6 ; 12/3=4; 12/-6=-2;加起来就是与x不互质的数的个数。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值