參考博客容斥原理(翻譯)
容斥原理是組合數學方法,可以求解集合、復合事件的概率等。
原理描述:
計算幾個集合並集的大小,先計算出所有單個集合的大小,減去所有兩個集合相交的部分,加上三個集合相交的部分,再減去四個集合相交的部分,以此類推,一直計算到所有集合相交的部分 。
維恩圖:
概率論:
事件Ai(i=1,...,n),P(Ai)為對應事件發生的概率。至少一個事件發生的概率:
容斥原理的證明:
B為Ai的集合。
假設某個任意元素在k個Ai集合中(k>=1),證明這個元素只被加了一次:
size(C)=1,該元素被加k次
size(C)=2,該元素被加(-1)^(2-1)* C(k,2)次
size(C)=3,該元素被加(-1)^(3-1)* C(k,3)次
...
size(C)=k,該元素被加(-1)^(k-1)* C(k,k)次
size(C)>k,該元素被加0次
計算總次數:
codeforces gym 101350G
題意:給出n*m的矩形,其中有k個炸彈(k<20),求出不包含炸彈的矩形的總個數。
思路:炸彈個數k很小,可以運用容斥,處理恰包含i(i=1,2,...,k)個炸彈(枚舉任意的i個炸彈,處理恰包含這i個炸彈的最小的矩形,公式處理。)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=55;
#define sa(x) scanf("%d",&x)
#define pr(x) printf("%d\n",x)
#define de(x) cout<<#x<<" = "<<x<<endl;
#define pb push_back
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
//--------------------------------------//
ll x[N],y[N];
ll n,m;
int k;
ll solve() {
ll x1,x2,y1,y2,ans=0;
for(int i=1;i<(1<<k);++i) {
ll bits=0;
ll res=1ll;
for(int j=0;j<k;++j) {
if((1<<j)&i) {
if(!bits) x1=x2=x[j],y1=y2=y[j];
else {
x1=min(x1,x[j]);
y1=min(y1,y[j]);
x2=max(x2,x[j]);
y2=max(y2,y[j]);
}
++bits;
}
}
ll t=x1*(n-x2+1)*y1*(m-y2+1);
if(bits&1) ans+=t;
else ans-=t;
}
return ans;
}
int main() {
int T;sa(T);
while(T--) {
scanf("%I64d%I64d%d",&n,&m,&k);
for(int i=0;i<k;++i) scanf("%I64d%I64d",&x[i],&y[i]);
ll ans=(1ll+n)*n*(1ll+m)*m/4ll-solve();
printf("%I64d\n",ans);
}
return 0;
}
hdu 4135
題意:區間[a,b]內與n互質的數的個數。
思路:將n質因數分解。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<iostream>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<vector>
using namespace std;
typedef long long ll;
const int N=1000005;
#define de(x) cout<<#x<<" = "<<x<<endl;
#define pb push_back
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
//--------------------------------------//
ll p[N];
int main() {
int T,cas=0; scanf("%d",&T);
ll a,b,r,ans1,ans2;
while(T--) {
scanf("%I64d%I64d%I64d",&a,&b,&r);
int pcnt=0;
ans1=ans2=0;
--a;
for(ll i=2;i*i<=r;++i) {//對r質因數分解
if(r%i==0) {
p[pcnt++]=i;
while(r%i==0) r/=i;
}
}
if(r>1) p[pcnt++]=r;
for(int i=1;i<(1<<pcnt);++i) {
ll mul=1;
ll bits=0;
for(int j=0;j<pcnt;++j) {
if(i&(1<<j)) {
mul*=p[j];
bits+=1ll;
}
}
if(bits&1) ans1+=a/mul,ans2+=b/mul;
else ans1-=a/mul,ans2-=b/mul;
}
ll ans=b-a+ans1-ans2;
printf("Case #%d: %I64d\n",++cas,ans);
}
return 0;
}