容斥原理专辑(1)
1、hdu 1796 ?How many integers can you find
容斥典型問題:求在給定區間內,能被給定集合至少一個數整除的數個數
方法:將給出的n個整除的數進行二進制枚舉(2^n),計算ai所能組成的各種集合(這里將集合中ai的最小公倍數作為除數)在區間中滿足的數的個數,然后利用容斥原理實現加減
注意:1、在給定的數中由于他們的關系不是互質的,所以要計算他們的lcm,而不是他們直接相乘 比如 2 4
? ? ? ? 2、在題目中說給定的數是非負的,所以在樣例會會出現0,在計算時要將其拿掉~
#include <iostream> #define ll long long using namespace std;ll gcd(ll a,ll b) {if(b==0) return a;else return gcd(b,a%b); }ll lcm(ll a,ll b) {return a*b/gcd(a,b); }int main() {int n,m,k;ll data[15],a[15];while(cin>>n>>m){for(int i=0;i<m;i++)cin>>data[i];k=0;for(int i=0;i<m;i++){if(data[i])a[k++]=data[i];}ll ans=0;for(int i=1;i<(1<<k);i++){int cnt=0;ll mul=1;for(int j=0;j<k;j++){if(i&(1<<j)){cnt++;mul=lcm(mul,a[j]);}}if(cnt&1) ans+=(n-1)/mul;else ans-=(n-1)/mul;}cout<<ans<<endl;}return 0; }?
2、hdu 4135 Co-prime
容斥典型問題:求指定區間內與n互素的數的個數
方法:首先利用逆向思維,求出與n不互質的個數
? ? ? ??考慮n的所有素因子pi(i=1…k)
?????????在[l,r]中有多少數能被pi整除呢?它就是:
? ? ? ????r/pi-(l-1)/pi
? ? ? ? ?由于有些數可能被統計多次(被好幾個素因子整除)。所以,我們要運用容斥原理來解決。
?????????我們可以用2^k的算法求出所有的pi組合,然后計算每種組合的pi乘積,通過容斥原理來對結果進行加減處理。
? ? ? ? ?結果返回 (l-r+1)-個數
#include <bits/stdc++.h> #define ll long longusing namespace std;ll solve(ll a,ll b,ll n) {vector <ll> p;for(int i=2;i*i<=n;i++){if(n%i==0){p.push_back(i);while(n%i==0)n=n/i;}}if(n>1) p.push_back(n);ll sum=0;for(int i=1;i<(1<<p.size());i++){ll cnt=0;ll mult=1;for(int j=0;j<p.size();j++){if(i&(1<<j)){cnt++;mult*=p[j];}}if(cnt&1) sum+=b/mult-(a-1)/mult;else sum-=b/mult-(a-1)/mult;}return (b-a+1)-sum; }int main() {int t,i=0;ll a,b,n;cin>>t;while(t--){cin>>a>>b>>n;i++;cout<<"Case #"<<i<<": ";cout<<solve(a,b,n)<<endl;}return 0; }?3、hdu 1695 GCD
容斥典型問題:求指定區間內與n互素的數的個數
歐拉函數問題:求指定區間內gcd(a,b)=x的對數
方法:將問題可以轉化為和時,的有序對的個數。
? ? ? ? 那么先比較和的?大小
? ? ? ? 相同的部分可以用歐拉函數的累加計算,在這里采用的篩法打表形式,進行預處理
? ? ? ? 沒有公共的部分用容斥計算,求公共的區間內與非公共區間的每一個數互素的數的個數
? ? ? ? 例如: 1 3 1 5 1
? ? ? ? 公共部分 【1,3】 歐拉求出1~3上的累加
? ? ? ? 非公共部分【4,5】 求出 【1,3】中與 4 互素的個數,求出 【1,3】中與 5 互素的個數,最后累加
注意:該題有 k=0 的情況,當k=0 時 ,結果直接為0 ,無需計算
#include <iostream> #include <vector> #define ll long longusing namespace std; const ll maxn=100001;ll eular[maxn];void init() {for(int i=0; i<maxn; i++)eular[i]=i;for(int i=2; i<maxn; i++){if(eular[i]==i){for(int j=i; j<maxn; j+=i)eular[j]=eular[j]/i*(i-1);}} }ll get_ans_1(int n) {ll sum=0;for(int i=1; i<=n; i++)sum+=eular[i];//cout<<sum<<endl;return sum; }ll get_ans_2(int smal,int big) {ll ans=0;//cout<<smal<<" "<<big<<endl;for(int i=smal+1; i<=big; i++){vector <ll> p;int n=i;ll sum=0;for(int j=2; j*j<=n; j++){if(n%j==0){p.push_back(j);while(n%j==0)n=n/j;}}if(n>1) p.push_back(n);// for(int j=0;j<p.size();j++)//cout<<p[j]<<endl;for(int j=1; j<(1<<p.size()); j++){int cnt=0;ll mul=1;for(int k=0; k<p.size(); k++){if(j&(1<<k)){cnt++;mul=mul*p[k];}}if(cnt&1) sum+=smal/mul;else sum-=smal/mul;}ans+=smal-sum;}return ans; }int main() {int t,i=0;int a,b,c,d,k;ll ans;init();cin>>t;while(t--){cin>>a>>b>>c>>d>>k;i++;ans=0;if(k==0){cout<<"Case "<<i<<": ";cout<<"0"<<endl;continue;}b=b/k;d=d/k;if(b<d)ans=get_ans_1(b)+get_ans_2(b,d);else if(b>d)ans=get_ans_1(d)+get_ans_2(d,b);else{ans=get_ans_1(b);}cout<<"Case "<<i<<": ";cout<<ans<<endl;}return 0; }?
4、hdu 2841?Visible Trees
容斥典型問題:求指定區間內與n互素的數的個數
方法:該題的重點是將應用問題轉化成數學問題,我們可以發現 gcd(a,b)=1 時可以看見它
? ? ? ? 這樣就從1~n枚舉找1~m與其互質的數
#include <iostream> #include <vector> #define ll long longusing namespace std; const ll maxn=100001;int main() {int t;ll ans;cin>>t;while(t--){int n,m;cin>>n>>m;ans=0;for(int i=2;i<=n;i++){vector <ll> p;ll sum=0;int t=i;for(int j=2;j*j<=t;j++){if(t%j==0){p.push_back(j);while(t%j==0)t=t/j;}}if(t>1) p.push_back(t);for(int j=1;j<(1<<p.size());j++){int cnt=0;ll mul=1;for(int k=0;k<p.size();k++){if(j&(1<<k)){cnt++;mul=mul*p[k];}}if(cnt&1) sum+=m/mul;else sum-=m/mul;}ans+=m-sum;}cout<<ans+m<<endl;}return 0; }?
5、hdu 3501?Calculation 2
容斥典型問題:求指定區間內與n互素的數的個數
方法:該問題是求小于n且與n不互質數的和為多少
? ? ? ? 我們可以先求出n的素因子, p1 p2 p3 …… pn
? ? ? ? 那么對于 p1 在小于 n 的數的范圍內有
? ? ? ? 1*p1 ?2*p1 ?3*p1 ?…… x*p1 與n不互質?
? ? ? ? 再根據等差數列的性質計算出它們的和,再利用容斥原理計算
#include <iostream> #include <vector> #define ll long longusing namespace std;const ll mod=1000000007;int main() {ll n;while(cin>>n&&n){if(n==1){cout<<"0"<<endl;continue;}int a=n;vector <ll> p;for(int i=2;i*i<=a;i++){if(a%i==0){p.push_back(i);while(a%i==0)a=a/i;}}if(a>1) p.push_back(a);ll ans=0;for(int i=1;i<(1<<p.size());i++){int cnt=0;int mul=1;for(int j=0;j<p.size();j++){if(i&(1<<j)){cnt++;mul=mul*p[j];}}ll num=(n-1)/mul;ll tmp=(mul+mul*num)*num/2;if(cnt&1) ans+=tmp;else ans-=tmp;}cout<<ans%mod<<endl;}return 0; }?
6、?UVA ?11806 ?Cheerleaders
方法:利用容斥原理可以轉到求“第一行、第一列、最后一行、最后一列沒有石子”的方案數。
? ? ? ? ?枚舉各個集合的組合時可以借助二進制進行枚舉
#include<iostream> #include<cstdio> #include<cstring> using namespace std;const int mod=1000007; int data[510][510];void get_data() {memset(data,0,sizeof(data));for(int i=0;i<=500;i++)data[i][0]=1;for(int i=1;i<=500;i++)for(int j=1;j<=i;j++)data[i][j]=(data[i-1][j]+data[i-1][j-1])%mod; }int main() {int t,p=0;cin>>t;get_data();while(t--){p++;int n,m,k;int ans=0;cin>>n>>m>>k;for(int i=0;i<16;i++){int cnt=0;int row=n,col=m;if(i&1) {row--;cnt++;}if(i&2) {row--;cnt++;}if(i&4) {col--;cnt++;}if(i&8) {col--;cnt++;}if(cnt%2==0) ans=(ans+data[row*col][k])%mod;else ans=(ans+mod-data[row*col][k])%mod;}cout<<"Case "<<p<<": ";cout<<ans<<endl;}return 0; }?
轉載于:https://www.cnblogs.com/nefu929831238/p/6042645.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
- 上一篇: OpenCV circle函数
- 下一篇: 部分标点符号和数学符号的英文名字