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 long using 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 long using 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 long using 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 long using 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; }