容斥原理练习
Tmutarakan Exams
题目大意
从1~s中选出k个数,使得这k个数的gcd>1,求共有多少种选法
题解
gcd可以为 2,3,4…
我们可以发现,gcd不可能是4,因为是4的倍数,也一定是2的倍数
所以,可以枚举gcd=素数
又因为这里的k,s<=50,所以,可以用一个数组保存小于s的质数
int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
当gcd =
2:有s/2个2的倍数,则选法为 C(s/2,k)
3:有s/3个3的倍数,则选法为 C(s/3,k)
…
但是,这其中的选择会有重复的,可以用容斥原理减去重复的即可
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define ll long long
const int N = 1e5+10;
vector<int> v;
int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
int C[55][55];
void init()
{
for(int i=0;i<52;i++) C[i][0] = C[i][i] = 1;
for(int i=1;i<52;i++)
{
for(int j=1;j<i;j++)
{
C[i][j] = C[i-1][j-1]+C[i-1][j];
}
}
}
void slove()
{
ll ans = 0;
int k,s;
cin >> k >> s;
for(int i=0;i<10;i++)
{
int cnt = s/prime[i];
if(cnt>=k) ans = ans + C[cnt][k];
}
ans -= C[s/6][k] ;
ans -= C[s/10][k] ;
ans -= C[s/14][k] ;
ans -= C[s/22][k] ;
ans -= C[s/15][k] ;
ans -= C[s/21][k] ;
if(ans > 10000)
ans = 10000 ;
cout << ans << endl;
}
int main()
{
init();
int t;
t = 1;
while(t--) slove();
}
The Lottery
对于此题,正着求不好求
可以转换为求1~n中至少被a[i]中一个整除的数的个数
而1~n中能被x整除的数的个数为 n/x
那么,结果即为 n - n/a1-n/a2…+n/lcm(a1,a2)…
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <cmath>
using namespace std;
using ll = long long;
ll a[20];
ll lcm(ll x,ll y)
{
return (ll)x/__gcd(x,y)*y;
}
int main()
{
ll n,m;
while(cin >> n >> m)
{
ll res = 0;
memset(a,0,sizeof a);
for(int i=0;i<m;i++) cin >> a[i];
for(int i=1;i<(1<<m);i++)
{
ll t = 1;
ll cnt = 0;
for(int j=0;j<m;j++)
{
if(i>>j&1)
{
cnt++;
t = lcm(t,a[j]);
}
}
if(cnt&1) res+=n/t;
else res-=n/t;
}
cout << n-res<<endl;
}
return 0;
}
Cheerleaders - UVA 11806 - Virtual Judge (vjudge.net)
题目大意
给一个n*m的网格以及k个Cheerleaders,问在满足下列条件下有多少种方法将这k个Cl安排在网格中的方法
1.网格的四个边界每个边界至少要有一个人----当一个人站在两个边界汇合处算站在两个边界上
2.每个格子只能占一个人
题解
先抛开限制条件1,很容易求出共有C(n*m,k)中安排方法
定义A,B,C,D为至少第一行,最后一行,第一列,最后一列没有安排人的情况
那么答案就C(n*m,k) - |A∪B∪C∪D|
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <cmath>
using namespace std;
using ll = long long;
const int N = 601 ,mod = 1000007;
ll C[N][N];
void init()
{
for(int i=0;i<N;i++) C[i][0] = C[i][i] = 1;
for(int i=1;i<N;i++)
{
for(int j=1;j<i;j++)
{
C[i][j] = (C[i-1][j] + C[i-1][j-1])%mod;
}
}
}
int main()
{
init();
int t;
cin >> t;
for(int r=1;r<=t;r++)
{
int row,col,k;
cin >> row >> col >> k;
ll res = C[row*col][k];
int u = 4;
for(int i=1;i<(1<<u);i++)
{
int n = row,m = col ;
int cnt = 0;
for(int j=0;j<u;j++)
{
if(i>>j&1)
{
cnt++;
if(j%2==0) m--;
else n--;
}
}
if(cnt&1) res-=C[n*m][k];
else res+=C[n*m][k];
res = (res%mod+mod)%mod;
}
printf("Case %d: %d\n",r,res);
}
return 0;
}
总结
1~n中为p(质数)的倍数的数的个数为 n/p
1~n中为x*y的倍数的数的个数为 n/lcm(x,y)
1~n中与x不互质的数的个数为
{
先将x进行质因数分解,得到集合P = {p1,p2,..}
然后利用容斥原理计算
n-n/p1-n/p2..+n/(p1*p2)...
}
(注:为什么不用欧拉函数来求解
---因为欧拉函数必须从1开始求,即若求x那么就必须要求出1~x
这样,当x特别大的时候TLE是必然的
)