容斥定理+组合计数

A-Ekka Dokka

Ekka and his friend Dokka decided to buy a cake. They both love cakes and that’s why they want to share the cake after buying it. As the name suggested that Ekka is very fond of odd numbers and Dokka is very fond of even numbers, they want to divide the cake such that Ekka gets a share of N square centimeters and Dokka gets a share of M square centimeters where N is odd and M is even. Both N and M are positive integers.

They want to divide the cake such that N * M = W, where W is the dashing factor set by them. Now you know their dashing factor, you have to find whether they can buy the desired cake or not.

Input

Input starts with an integer T (≤ 10000), denoting the number of test cases.

Each case contains an integer W (2 ≤ W < 263). And W will not be a power of 2.

Output

For each case, print the case number first. After that print "Impossible" if they can't buy their desired cake. If they can buy such a cake, you have to print N and M. If there are multiple solutions, then print the result where M is as small as possible.

Sample Input

3

10

5

12

Sample Output

Case 1: 5 2

Case 2: Impossible

Case 3: 3 4

这道题的题意就是将一个数分解成一个奇数和一个偶数,并且要求这个偶数尽可能小。
如果输入的这个数是一个奇数的话,直接输出impossible,如果这个数是一个偶数的话,它一定可以分解成一个偶数和一个奇数。

#include<iostream>
using namespace std;
typedef long long ll;
int main()
{
	int T,cnt=1;
	cin>>T;
	while(T--){
		ll x,n,m;
		cin>>x;
		if(x&1) cout<<"Case "<<cnt++<<": "<<"Impossible"<<"\n";
		else{
			ll ans=1;
			while(x%2==0){      //一直取到x是奇数为止
				x=x/2;
				ans*=2;
			}
			cout<<"Case "<<cnt++<<": "<<x<<" "<<ans<<"\n"; 
		}
	}
	return 0;
}

B - How many integers can you find

Now you get a number N, and a M-integers set, you should find out how many integers which are small than N, that they can divided exactly by any integers in the set. For example, N=12, and M-integer set is {2,3}, so there is another set {2,3,4,6,8,9,10}, all the integers of the set can be divided exactly by 2 or 3. As a result, you just output the number 7.

Input
There are a lot of cases. For each case, the first line contains two integers N and M. The follow line contains the M integers, and all of them are different from each other. 0<N<2^31,0<M<=10, and the M integer are non-negative and won’t exceed 20.

Output
For each case, output the number.

Sample Input

12 2
2 3

Sample Output

7

这道题 的题意就是给定一个范围,这个范围是1~N,再给M个数,求出M个数在1到N的范围内有多少个倍数,并输出个数。例如2,3在1到12 的范围内有2,3,4,6,8,9,10这7个数,所以输出7.
这道题就是用范围内的所有数减去给定的M个数的倍数的个数,例如12,给定2和3,12/2-1=5,12/3-1=3,2和3的最小公倍数是6,再用12/6-1=1,只需要5+3-1=7.,这个最小公倍数代表的是,这个数被计算了多次,需要减去多余的次数。

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int a[20],m;
ll n,SUM;
ll gcd(ll a,ll b)     
{
	return b?gcd(b,a%b):a; 
}

ll lcm(ll a,ll b)
{
	return a/gcd(a,b)*b;
}

int main()
{
	while(~scanf("%d %d",&n,&m))
	{
		SUM=0,n--;    //给定的范围不包括边界,所以需要n减1
		for(int i=0;i<m;i++)
		{
			scanf("%d",&a[i]);
			if(a[i]==0)
			{
				i--,m--;
			} 
		 }
		for(int i=0;i<(1<<m);i++)     //二进制枚举
		{
			ll sum=1;
			int cnt=0,res=-1;
			for(int j=0;j<m;j++)
			{
				if(1&(i>>j))
				{
					sum=lcm(sum,a[j]);
					cnt++;
				}
			}
			if(cnt%2==1) res=1;
			if(cnt) SUM=SUM+(n/sum)*res;
		 } 
		printf("%lld\n",SUM); 
	}
	return 0;
}

C - Co-prime

Given a number N, you are asked to count the number of integers between A and B inclusive which are relatively prime to N.
Two integers are said to be co-prime or relatively prime if they have no common positive divisors other than 1 or, equivalently, if their greatest common divisor is 1. The number 1 is relatively prime to every integer.

Input
The first line on input contains T (0 < T <= 100) the number of test cases, each of the next T lines contains three integers A, B, N where (1 <= A <= B <= 1015) and (1 <=N <= 109).

Output
For each test case, print the number of integers between A and B inclusive which are relatively prime to N. Follow the output format below.

Sample Input

2
1 10 2
3 15 5

Sample Input

2
1 10 2
3 15 5

这道题的意思就是在给定的区间内找出与N互质的数的个数,1也算是相对质数,用到容斥定理,奇加偶减

#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=10000;
ll a,b,n,mp[maxn];
int cnt;

void pre()       //分解质因子函数
{
	cnt=0;
	for(ll i=2;i*i<=n;i++)         
	{
		if(n%i==0){
			mp[cnt++]=i;
			while(n%i==0)
				n=n/i; 
		}
	 } 
	if(n!=1) mp[cnt++]=n; 
}

ll slove(ll x)          //二进制枚举函数
{
	ll ans=0;
	for(int i=1;i<(1<<cnt);i++){
		ll res=1;
		int num=0;
		for(int j=0;j<cnt;j++)
		{
			if(i&(1<<j)){
				num++;          //num是计数的,计算当前的有几个数
				res*=mp[j];
			}
		}
		if(num&1) ans+=x/res;      //x代表的是从1到x,也就是一个范围吧*
		else ans-=x/res;      //遵循着奇加偶减的规律
	}
	return ans;
}

int main()
{
	int t,temp=0;
	cin>>t;
	while(t--)
	{
		cin>>a>>b>>n;
		pre();
		printf("Case #%d: %lld\n",++temp,b-a+1-slove(b)+slove(a-1));//b-a+1代表着a到b范围内的数,减去a到b范围之间的不是素数的个数。
	}
}
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;

int main(){
	ll A,B,N,T,a[1000];
	scanf("%lld",&T);
	int cnt1=1;
	while(T--){
		scanf("%lld%lld%lld",&A,&B,&N);
		int time=0;
		for(int i=2;i*i<=N;i++){		//质因子分解 
			if(N%i==0) a[++time]=i;
			while(N%i==0) N=N/i;
		}
		if(N!=1) a[++time]=N;
		ll SUM =0;
		for(int i=1;i<(1<<time);i++)		// 二进制枚举
		{
			ll sum=1, cnt =0;
			for(int j=0;j<time;j++)
			{
				if(1&(i>>j)){
					sum *=a[j+1];	
					cnt ++;
				}					
			} 
			if(cnt & 1){
				SUM=SUM+B/sum;	
				SUM=SUM-(A-1)/sum;
			}
			else{
				SUM=SUM-B/sum;
				SUM=SUM+(A-1)/sum;
			}	
		}
		printf("Case #%d: %lld\n",cnt1++,B-(A-1)-SUM);
	}
	return 0;
}

两种方法一样,只不过是第一种方法是利用分段函数进行编写,第二个直接在main函数中

D - 计算组合数

给出两个非负整数 n 和 m ,编写程序计算组合数 C(n,m)

的值。
输入格式

一行两个空格隔开的非负整数 n,m

输出格式
输出一个数表示 C(n,m)

数据范围
0<n≤20,0≤m≤n

Sample Input

5 2

Sample Output

10

这道题就直接进行计算就行。
方法一:
C[n][m],n<=15;

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;   

ll C(int a,int b){
	if(b>a) return 0;
	if(b>a-b) b=a-b;    //这里的重新赋值可以减小时间复杂度
	ll m=1,s=1;
	for(int i=0;i<b;i++){
		m*=(a-i);     //这个m是分母
		s*=(i+1);       //s为分子
	}
	return m/s;
}
int main()
{
	int n,m;
	cin>>n>>m;
	cout<<C(n,m)<<"\n";
	return 0;
}

方法二:
利用等式c[n][m] = (c[n - 1][m] + c[n - 1][m - 1])
但是这个的数组的最大时1e3,超过就会爆

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=50;
ll a[N][N];
void cal()
{
	a[1][1]=1;
	for(int i=0;i<N;i++) a[i][0]=1;    //递归
	for(int i=1;i<N;i++)
		for(int j=1;j<=i;j++)
			a[i][j]=a[i-1][j]+a[i-1][j-1];
}

int main()
{
	cal();
	int n,m;
	cin>>n>>m;
	cout<<a[n][m]<<"\n";
	return 0;
}

如果数很大的话,可以使用lucas定理进行解题
lucas定理模板:
C(n, m) = C(n%p, m % p) * C(n / p, m/ p) (mod p)

const int mod=xxx;  //定义mod的值

ll ksm(ll a,ll b)      //快速幂
{
	ll ans=1;
	while(b){
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	} 
	return ans;
}

ll C(ll n,ll k)      
{
	if(k>n) return 0;
	if(k>n-k) k=n-k;    //减小时间复杂度
	ll m=1,s=1;
	for(int i=1;i<=k;i++){
		m=m*(n-i+1)%mod;
		s=s*i%mod;
	}
	return m*ksm(s,mod-2)%mod;    //这里的ksm使用的时费马小定理,即如果a与p时互质的,则a^(p-2) ≡ a^(-1)(mod p) ,这里时使用的逆元。
}

ll lucas(ll a,ll b){  //卢卡斯定理的使用
	if(a<mod && b<mod) return C(a,b);
	else return C(a%mod,b%mod)*lucas(a/mod,b/mod)%mod; 
}

E - Binomial Showdown

In how many ways can you choose k elements out of n elements, not taking order into account?
Write a program to compute this number.

Input
The input will contain one or more test cases.
Each test case consists of one line containing two integers n (n>=1) and k (0<=k<=n).
Input is terminated by two zeroes for n and k.

Output
For each test case, print one line containing the required number. This number will always fit into an integer, i.e. it will be less than 231.
Warning: Don’t underestimate the problem. The result will fit into an integer - but if all intermediate results arising during the computation will also fit into an integer depends on your algorithm. The test cases will go to the limit.

Sample Input

4 2
10 5
49 6
0 0

Sample Output

6
252
13983816

这道题就是组合数的计算,只不过需要注意计算过程中的范围,如果暴力的话,中间是会爆错的,所以需要边乘边除

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ull;

ull C(ull a,ull b){
	if(b>a) return 0;
	if(b>a-b) b=a-b;
	ull ans=1;
	for(int i=1;i<=b;i++){
		ans*=(a-i+1);     //这里需要先乘后除保障不会溢出
		ans/=i;
	} 
	return ans;
}

int main(){
	ull n,m;
	while(~scanf("%lld %lld",&n,&m)){
		if(n==0 && m==0) break;
		printf("%lld\n",C(n,m));
	}	
	return 0;
}

F - 组合数

蒜头君萌上了组合数,现在他很想知道 n∑i=0Cin 是多少,其中 i 为偶数,Cin 是组合数,表示 n 个物品无顺序选取 i个的方案数。
由于答案可能很大,请输出答案对 6662333的余数

输入格式
一个整数 n (1≤n≤1018)

输出格式
一个整数,表示答案对 6662333
取模的结果。

Sample Input

5

Sample Output

16

这题的规律就是偶数和与奇数和是相等的,都等于2^(n-1).
所以这道题只需要使用快速幂就可以了

#include<iostream>
using namespace std;
typedef long long ll;
const ll mod=6662333;
ll poww(ll a,ll b)     //快速幂
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%mod;    //每次取模
		a=a*a%mod;
		b>>=1;	
	} 
	return ans%mod;
}

int main()
{
	ll a;
	cin>>a;
	cout<<poww(2,a-1)<<"\n";
	return 0;
}

G - 求组合数

从 n 个不同元素中,任取 m(m≤n) 个元素并成一组,叫做从 n 个不同元素中取出 m 个元素的一个组合;从 n 个不同元素中取出 m(m≤n) 个元素的所有组合的个数,叫做从 n 个不同元素中取出 m 个元素的组合数,记为 Cmn

组合数的计算方法为 Cmn=n!m!(n−m!)
,其中 ! 表示阶乘。同时组合数还满足 Cmn=Cm−1n−1+Cmn−1这个性质。

请你编程求解组合数 Cmn
的值,由于结果可能很大,输出结果对 1000000007取模的结果。

输入格式
输入一行两个整数 n(1≤n≤2000),m(0≤m≤n)

输出格式
输出 Cmn对 1000000007 取模的结果。

Sample Input

5 2
100 50

Sample Output

10
538992043

计算组合数然后进行取模,由于n是0~2000范围内,数据大,使用lucas定理进行解题
直接套用模板即可

#include<iostream>
using namespace std;
typedef long long ll;
const ll mod= 1000000007;

ll ksm(ll a,ll b)
{
	ll ans=1;
	while(b){
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	} 
	return ans;
}

ll C(ll n,ll k)
{
	if(k>n) return 0;
	if(k>n-k) k=n-k;
	ll m=1,s=1;
	for(int i=1;i<=k;i++){
		m=m*(n-i+1)%mod;
		s=s*i%mod;
	}
	return m*ksm(s,mod-2)%mod;
}

ll lucas(ll a,ll b){
	if(a<mod && b<mod) return C(a,b);
	else return C(a%mod,b%mod)*lucas(a/mod,b/mod)%mod; 
}

int main()
{
	ll n,m;
	cin>>n>>m;
	cout<<lucas(n,m)<<"\n";
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值