银联高校极客挑战赛 复赛(A,B,思维+数学)

A

题目描述

现在已经是暑假了!这周日,码队的弟弟所在的班级——上海某中学高二 33 班的同学们准备在码队弟弟的带领下(码队的弟弟是这个班的班长),举办一场派对。

码队的弟弟让两名同学打开手机 「云闪付」app,在云闪付在线商城上采购饮料。经过这两名同学统计,发现一共需要买 nn 瓶饮料。而在云闪付在线商城上,一共有 mm 种不同的饮料(包括「肥宅快乐水」,并假设云闪付在线商城上的每种饮料的购买数量都没有限制)。由于码队的弟弟喜欢喝「肥宅快乐水」,所以这两名同学决定至少要买一瓶「肥宅快乐水」。

这样看来,饮料购买的方案实在是太多了!两位同学突发奇想,想让你帮忙计算:总共有多少种购买饮料的方案。(答案对 10^9 + 7109+7 取模,同种饮料都是一样的,不作区分。)

输入格式

有多组数据。

第一行输入一个整数 TT, 表示有 TT 组数据( 1\le T \le 10^41≤T≤104 )。

对于每一组测试数据:输入一行,包含两个整数 n, mn,m,以空格分隔,分别表示这两位同学需要买 nn 瓶饮料(其中必须有一瓶是「肥宅快乐水」),云闪付在线商城中一共有 mm 种包括「肥宅快乐水」在内的不同的饮料。(1 \le n,m \le 10^31≤n,m≤103)

输出格式

对于每一组测试数据,输出一行。

每行只包含一个整数,表示有多少种购买饮料的方案。

提示

假设我们用 FF 表示「肥宅快乐水」,用 AA 表示某种其他饮料。


针对样例输入 1:


对于第一组测试数据,只有 11 种购买饮料的方案:(FF)。


对于第二组测试数据,有 22 种购买饮料的方案:(FF, FF),(FF, AA)。

输出时每行末尾的多余空格,不影响答案正确性

样例输入1复制

2
1 1
2 2

样例输出1复制

1
2

样例输入2复制

3
1 2
2 3
3 3

样例输出2复制

1
3
6

真菜!!只做出了A题~~~这里写一下A,B题,题解~~

题意:买n瓶水有m种,每种有很多,至少要买一瓶肥宅快乐水,其他的随便买,问多少种方法

题解:经典隔板法,我跟官方题解方法不一样,我是每次枚举买几种,在这一类中有多少方法,进行分步乘法求和即可~~

上代码:

#include 
#include 
using namespace std;
typedef long long ll;
const int MAX = 1e6+100;
const int mod = 1e9+7;
ll f[MAX],finv[MAX],inv[MAX];//f是阶乘,finv是逆元的阶乘
void init(){
	inv[1]=1;
	for (int i = 2; i < MAX;i++){
		inv[i]=(mod-mod/i)*1ll*inv[mod%i]%mod;
	}
	f[0]=finv[0]=1;
	for (int i = 1; i < MAX;i++){
		f[i]=f[i-1]*1ll*i%mod;
		finv[i]=finv[i-1]*1ll*inv[i]%mod;
	}
}
ll comb(int n,int m){//comb(n, m)就是C(n, m)
	if(m<0||m>n) return 0;
	return f[n]*1ll*finv[n-m]%mod*finv[m]%mod; 
}
int main(){
	init();
	int t;
	scanf("%d",&t);
	while(t--){
		int n,m;
		scanf("%d%d",&n,&m);
		ll ans=0;
		n--;//去掉必须买的那一瓶
		for (int i = 1; i <= m && i <= n;i++){
			ans=(ans+comb(m,i)*comb(n-1,i-1))%mod;
		}
		if(ans==0) ans=1;//如果n是1就只有一种方法,因为n减1后是0,所以特判一下
		printf("%lld\n",ans);
	}
	return 0;
}

B

题目描述

码队的弟弟喜欢做数学题。这不,听说你也喜欢做数学题,码队的弟弟非常高兴,决定立刻送给你一道数学题,请你完成。

给定三个整数 n,m,pn,m,p ,求满足 a \times b = k \times pa×b=k×p (( 1 \le a \le n, 1 \le b \le m, k1≤a≤n,1≤b≤m,k为任意正整数 )) 的整数对 (a,b)(a,b) 的数量。

输入格式

有多组数据。

第一行输入一个整数,表示有 TT 组数据( 1 \le T \le 101≤T≤10 )。

对于每一组测试数据:输入一行,包含三个整数 n,m,pn,m,p ,以空格分隔( 1 \le n,m \le 10^9, 1 \le p \le 10^51≤n,m≤109,1≤p≤105 )。

输出格式

对于每一组数据,输出一行。

每行只包含一个整数,表示满足条件的整数对的数量。

提示

针对样例输入1:一共有 55 个整数对,分别是 (1,3),(2,3),(3,3),(3,1),(3,2)(1,3),(2,3),(3,3),(3,1),(3,2)。

输出时每行末尾的多余空格,不影响答案正确性

样例输入1复制

1
3 3 3

样例输出1复制

5

样例输入2复制

1
10 20 15

样例输出2复制

29

题意:输入n,m,p 求a*b=k*p (k是任意正整数,a是1-n,b是1-m)有多少对(a,b) 满足这个式子

题解:这是个经典问题,自己太菜,没接触过,官方题解写的很清楚,我再加点解释,

银联高校极客挑战赛 复赛(A,B,思维+数学)_第1张图片

a的构造方法,很奇妙,任意一个数都可以写成这种形式,x=n/p,是因为构造形式,显而易见,最后解释一下第(3)中为什么是到 x-1 因为在第三种条件下,k1*x+r>n,即a>n,不符合题意,解释完毕,然后计数就是用 k1的种类数*b的种类数(因为k1的种类数,代表了a的种类数,即x或者x+1)上代码:

#include 
#include 
#include 
using namespace std;
typedef long long ll;//要用ll,下面相乘会超int
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		ll n,m,p;
		scanf("%lld%lld%lld",&n,&m,&p);
		ll x=n/p;
		ll ans=0;
		for (ll r = 0; r < p;r++){
			ll w=m/(p/__gcd(r,p));//b的种类数
			if(r==0) ans+=x*w;//第一种情况,a是x种
			else if(r>0&&r<=n%p) ans+=(x+1)*w;//第二种情况,a是x+1种
			else ans+=x*w;//第三种情况,a是x种
		}
		printf("%lld\n",ans);
	}
	return 0;
}

 

你可能感兴趣的:(数论)