容斥原理
最近,老师给我们做有关容斥原理的题,做得一脸懵逼……
于是开始学容斥原理!!!
以下是关于其的百度网址:
选取大概内容:在计数时,必须注意没有重复,没有遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。
以下举个例子:
问100以内有多少个数是2的倍数?
或许你很快回答100/2=50个!!!
那再问100以内有多少个数是2、3的倍数?
或许你会和上问一样很快回答:100/2+100/3=50+33=83个,
但很明显这是错的:因为同时是2和3的倍数的数,算了2次,所以答案应为:83-100/6
=83-16=67
那最后问100以内有多少个数是2、3、5的倍数?
你会发现答案为:100/2+100/3+100/5-100/6-100/15/+100/30,观察符号符号不难发现:100/a,,为g个数的最小公倍数,当g是奇数时,符号为加;偶数时,符号为减号!
简而言之:
奇加偶减
最后附上例题:给出一个数组a[1..n], 问在区间[L,R]内有多少个数,至少能被a中的一个数整除。
多组测试数据。
第一行,一个整数g,表示有g组测试数据。 1 <= G <= 10
每组测试数据格式:
第一行,三个整数,N,L,R。 1 <= L<=R <= 10^9, 1<=N<=18。
第二行,N个整数,第i个整数是a[i]。 1 <= a[i] <= 10^9。
就是运用到二进制枚举,部分和,最大公约数和最小公倍数,(辗转相除法)的知识
#include<iostream>
#include<stdio.h>
#include<fstream>
using namespace std;
long long ans,n,l,r,a[20+10]; int g; bool ok; long long gcd(int a,int b)//辗转相除法 { if (b==0)return a; return gcd(b,a%b); } int main() { scanf("%d",&g); for (int z=1;z<=g;z++) { ans=0; scanf("%lld%lld%lld",&n,&l,&r); for (int i=1;i<=n;i++)scanf("%lld",&a[i]); for (int i=1;i<(1<<n);i++)//二进制枚举选哪几个数的最小公倍数 { int cnt=0; long long sum=1; ok=1; for (int j=0;j<n;j++) { if (i&(1<<j)) { cnt++,sum=sum*a[j+1]/gcd(sum,a[j+1]); if (sum>r)//优化,当最小公倍数的大于r就不会存在解 { ok=0; break; } } } if (ok) {
//求区间l~r之间有多少个数是sum的倍数=r及其以内是sum的倍数的数-(l-1)及其以内是sum的倍数的数
if (cnt&1)//判选取的个数:奇加偶减 {
ans+=(r/sum-(l-1)/sum); } else { ans-=(r/sum-(l-1)/sum); } } } printf("%lld\n",ans); } return 0; }