容斥原理练习

文章介绍了如何在编程竞赛中使用容斥原理解决关于选择数字的问题,涉及gcd计算、质数枚举和组合计数。通过实例展示了如何计算特定条件下的选数方案数量,以及处理边界和重复情况。同时,提到了用数论方法解决其他相关问题,如求解倍数个数和非互质数个数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

容斥原理练习

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是必然的
)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值