Live2D

容斥原理

最近,老师给我们做有关容斥原理的题,做得一脸懵逼……

于是开始学容斥原理!!!

以下是关于其的百度网址

选取大概内容:在计数时,必须注意没有重复,没有遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。

以下举个例子:

问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; }

 

posted @ 2019-01-22 15:55  GDFS均均  阅读(316)  评论(0编辑  收藏  举报