冬训第十七组数学专题答案

A - A^B Mod C

给出3个正整数A B C,求A^B Mod C。

例如,3 5 8,3^5 Mod 8 = 3。

Input

3个正整数A B C,中间用空格分隔。(1 <= A,B,C <= 10^9)

Output

输出计算结果

Sample Input

3 5 8

Sample Output

3

运用快速幂求出结果

#include <bits/stdc++.h>
using namespace std;
long long quickmi (int a,int b,int c) {
	if (b==1) {
		return a%c;
	} else {
		if (b%2==0) {
			long long result=quickmi(a,b/2,c);
			return result*result%c;
		} else {
			long long result=quickmi(a,b/2,c);
			result=result*result%c;
			result=result*a%c;
			return result;
		}
	}
}
int main()
{
	int a,b,c;
	cin>>a>>b>>c;
	long long x=quickmi(a,b,c);
	cout<<x;
	return 0;
} 

B - 逆元

阿克克希是求婚总动员的队长,他通过自己的双手,成就了无数年轻人的梦,但他却留下了悲伤的泪水。

求婚是非常费力的,他手上有 P-1P−1 个求婚请求,这 ii 个人的编号为 [1,P-1][1,P−1]

面对第 ii 个人他的求婚麻烦值为:ii 在模 PP 意义下的逆元。

他现在想知道总的麻烦值。

tips:tips: 如果有任意一个编号 ii 在模 PP 意义下不存在逆元,请输出 AKCniubiAKCniubi

输入格式

一行一个数 PP 表示求婚请求总数

输出格式

一行一个数表示总麻烦值

若有数存在无逆元的情况,输出 AKCniubiAKCniubi

数据范围

对于 30\%30% 的数据,P<=1000000

对于 50\%50% 的数据,P<=10000000

对于 100\%100% 的数据,P<=2^31

思路

首先应先明白什么是逆元,可以参考我前天发的博客

接着就是这题算一个加一个逆元会导致时间超限,我们通过测试几组数据就可以发现,它的逆元和就等于它自身的和(也就根本不用求逆元,只需要算出自身的和即可)明白这些就可以做题了

#include <bits/stdc++.h>
using namespace std;
int isprime (long long n) {
	long long i;
	for (i=2;i<=sqrt(n);i++) {
		if (n%i==0) {
			return 0;
		}
	}
	return 1;
}
long long niyuan(long long x,long long mod) {//这里采用了递归的方法,也可以采用其他方法,当然明白规律后也可以不要这些
	return x==1?1:(mod-mod/x)*niyuan(mod%x,mod)%mod;
}
int main()
{
	long long n;
	cin>>n;
	if (isprime(n)==0) {
		cout<<"AKCniubi"<<endl;
	} else {
		long long i,sum=0;
//		for (i=1;i<n;i++) {
//			i=i%n;
//			sum+=niyuan(i,n);
//		}
		sum=(1+n-1)*(n-1)/2;
		cout<<sum<<endl;
	}
	return 0;
}

C - 判决素数个数

输入两个整数X和Y,输出两者->>之间<<-的素数个数(包括X和Y)。

Input

两个整数X和Y(1 <= X,Y <= 105)。

Output

输出一个整数,表示X,Y之间的素数个数(包括X和Y)。

Sample Input

1 100

Sample Output

25

思路

由于数据规模较大,这题想暴力来解决会超时,所以我们采用素数筛

#include <bits/stdc++.h>
using namespace std;
int a[100010],b[100010];
int main()
{
	int i,n,t;
	a[1]=1;
	for (i=2;i<=100000;i++) {
		if (a[i]==0) {
			for (t=2*i;t<=100000;t+=i) {
				a[t]=1;
			}
		}
	}
	int x,y,cont=0;
	cin>>x>>y;
	if (x>y) {
		n=x;
		x=y;
		y=n;
	}
	for (i=1;i<=100000;i++) {//前缀和解法 
		if (a[i]==0) {
			b[i]=b[i-1]+1;
		} else {
			b[i]=b[i-1];
		}
	} 
//	for (i=x;i<=y;i++) {
//		if (a[i]==0) {
//			cont++;
//		}
//	}
	cout<<b[y]-b[x-1]<<endl;
	return 0;
}

D - 矩阵乘法

计算两个矩阵的乘法。n×m 阶的矩阵 A 乘以 m×k 阶的矩阵 B 得到的矩阵 C 是 n×k 阶的,且 C[i][j] = A[i][0] * B[0][j] + A[i][1] * B[1][j] + …… +A[i][m-1]* B[m-1][j](C[i][j]C[i][j]=A[i][0]×B[0][j]+A[i][1]×B[1][j]+……+A[i][m−1]×B[m−1][j](C[i][j] 表示 CC 矩阵中第 ii 行第 jj 列元素)。

输入格式

第一行为 n, m, kn,m,k,表示 AA 矩阵是 nn 行 mm 列,BB 矩阵是 mm 行 kk 列,n, m, kn,m,k 均小于 100100;

然后先后输入 AA 和 BB 两个矩阵,AA 矩阵 nn 行 mm 列,BB 矩阵 mm 行 kk 列,矩阵中每个元素的绝对值不会大于 10001000。

输出格式

输出矩阵 C,一共 n 行,每行 k 个整数,整数之间以一个空格分开。

Sample Input

3 2 3
1 1
1 1
1 1
1 1 1
1 1 1

Sample Output

2 2 2
2 2 2
2 2 2

思路

找规律直接求解即可

#include <bits/stdc++.h>
using namespace std;
int a[110][110],b[110][110],c[110][110];
int main()
{
	int n,m,k;
	cin>>n>>m>>k;
	int i,t,j;
	for (i=0;i<n;i++) {
		for (t=0;t<m;t++) {
			cin>>a[i][t];
		}
	}
	for (i=0;i<m;i++) {
		for (t=0;t<k;t++) {
			cin>>b[i][t];
		}
	}
	for (i=0;i<n;i++) {
		for (t=0;t<k;t++) {
			int sum=0;
			for (j=0;j<m;j++) {
				sum+=a[i][j]*b[j][t];
			}
			c[i][t]=sum;
			cout<<c[i][t];
			if (t==k-1) {
				cout<<endl;
			} else{
				cout<<" ";
			}
		}
	}
	return 0;
}

E - Bash游戏

有一堆石子共有N个。A B两个人轮流拿,A先拿。每次最少拿1颗,最多拿K颗,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N和K,问最后谁能赢得比赛。

例如N = 3,K = 2。无论A如何拿,B都可以拿到最后1颗石子。

Input

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000) 第2 - T + 1行:每行2个数N,K。中间用空格分隔。(1 <= N,K <= 10^9)

Output

共T行,如果A获胜输出A,如果B获胜输出B。

Sample Input

4
3 2
4 2
7 3
8 3

Sample Output

B
A
A
B

思路

属于博弈论题目,大家可以参考博弈总结

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int m;
	cin>>m;
	while (m--) {
		int n,k;
		cin>>n>>k;
		if (n%(k+1)==0) {
			cout<<"B"<<endl;
		} else {
			cout<<"A"<<endl;
		}
	}
	return 0;
}

F - 取石子游戏

有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。

Input

输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。

Output

输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。

Sample Input

2 1
8 4
4 7

Sample Output

0
1
0

思路

同样属于博弈论

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int m,n,x,y;
	while (scanf("%d %d",&m,&n)!=EOF) {
		if (m>n) {
			x=m;
			m=n;
			n=x;
		}
		x=n-m;
		y=(int)((1.0+sqrt(5.0))/2*1.0*x);
		if (y==m) {
			cout<<"0"<<endl;
		} else {
			cout<<"1"<<endl;
		}
	}
	return 0;
}

G - Matches Game

这是一个简单的游戏。在这个游戏中,有几堆火柴和两名玩家。这两个玩家轮流进行。在每一轮中,玩家可以选择一堆并从堆中取出任意数量的火柴(当然,取出的火柴数量不能为零,也不能大于所选堆中的火柴数量)。如果一名玩家取完火柴后,没有剩下火柴,该玩家就是赢家。假设这两个玩家都会作出最优决策。你的工作是判断先手玩家能否赢得比赛。

Input

输入由几行组成,每行中都有一个测试用例。 在一行的开头,有一个整数M(1<=M<=20),它是火柴堆的数量。然后是M个不大于10000000的正整数。这些M个整数表示每堆中的火柴数。

Output

对于每个测试用例,如果第一个玩家获胜,在一行中输出“Yes”,否则输出“No”。

Sample Input

2 45 45
3 3 6 9

Sample Output

No
Yes

思路

同样是博弈题目

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	while (scanf("%d",&n)!=EOF) {
		int a[30],i;
		for (i=0;i<n;i++) {
			cin>>a[i];
		}
		int sum=0;
		for (i=0;i<n;i++) {
			sum=sum^a[i];
		}
		if (sum==0) {
			cout<<"No"<<endl;
		} else {
			cout<<"Yes"<<endl;
		}
	}
	return 0;
}

H - 互质数的个数(一)

这里我们定义 φ(n) 表示所有小于等于 n 与 n 互质数的个数。

例如φ(10)=4,因为我们可以在 1∼10 中找到 1,3,7,9 与 10 互质。

输入格式

第一行输入一个整数 t,表示测试数据组数。

接下来 t 行,每行有一个整数 n。

输出格式

对于每组测试数据输出φ(n) 。

数据范围

1≤t≤100,1≤n≤10^10。

Sample Input

3
2
10
100

Sample Output

1
4
40

思路

想要暴力解决就会超时,这里我们引入欧拉公式

大家可以参考欧拉公式

#include <bits/stdc++.h>
using namespace std;
long long oula (long long n) {
	long long x=n;
	int i;
	for (i=2;i<=sqrt(n);i++) {
		if (n%i==0) {
			x=x/i*(i-1);
			while (n%i==0) {
				n/=i;
			}
		}
	}
	if (n>1) {
		x=x/n*(n-1);
	}
	return x;
}
int main ()
{
	int m;
	cin>>m;
	while (m--) {
		long long n,x;
		cin>>n;
		x=oula(n);
		cout<<x<<endl;
	}
	return 0;
}

I - Sumdiv

有两个自然数a和b(a,b≤50000000)

求a的b次方的所有约数之和模9901

输入格式

一行,包含由空格分隔的两个自然数a和b

输出格式

一行,a的b次方的约数和模9901

样例输入

2 3

样例输出

15

样例解释

8的约数是1,2,4,8, 它们的总和是15

15模9901是15

思路

大家可以参考乘法逆元+约数和

#include <bits/stdc++.h>
using namespace std;
const int mod=9901;
int a,b,m=0;
int c[1000010],d[1000010];
void divide (int a) {
	int i;
	for (i=2;i*i<=a;i++) {
		if (a%i==0) {
			c[++m]=i;
			d[m]=0;
			while (a%i==0) {
				a/=i;
				d[m]++;
			}
		}
	}
	if (a>1) {
		c[++m]=a;
		d[m]=1;
	}
}
long long quickmi (int a,long long b) {
	if (b==1) {
		return a%mod;
	} else {
		if (b%2==0) {
			long long result=quickmi(a,b/2);
			return result*result%mod;
		} else {
			long long result=quickmi(a,b/2);
			result=result*result%mod;
			result=result*a%mod;
			return result;
		}
	}
}
int main()
{
	long long f=1;
	cin>>a>>b;
	divide(a);
	int i;
	for (i=1;i<=m;i++) {
		long long x,y;
		if ((c[i]-1)%mod==0) {
			f=(f%mod*((long long)b*d[i]+1)%mod)%mod;
			continue;
		}	
		x=quickmi(c[i],(long long)b*d[i]+1);
		y=quickmi(c[i]-1,mod-2);
		x=(x-1+mod)%mod;
		f=((long long)f*x%mod*y%mod)%mod;
	}
	cout<<f<<endl;
	return 0;
}

J - The Lottery

给出n , m,和m个数a[1]⋯a[m]。

求1⋯n中不被a[1]⋯a[m]中任意一个整除的数的个数。
10⩽n<2^{31}231,1⩽m⩽15

输入格式

每组数据以n,m为第一行。

第二行m个数,表示a[i]。

输入文件以EOF结尾。

输出格式

每组数据一行一个数字表示答案。

样例输入

10 2
2 3
20 2
2 4

样例输出

3
10

思路

运用二进制枚举和容斥定理

可参考

 二进制枚举

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd (ll a,ll b) {//最大公约数
	return b?gcd(b,a%b):a;
} 
ll lcm (ll a,ll b) {//最小公倍数
	return a*b/gcd(a,b);
}
int main()
{
	ll n,i,t,a[20];
	int m;
	while (cin>>n>>m) {
		for (i=0;i<m;i++) {
			cin>>a[i];
		}
		ll sum=0;
		for (i=1;i<(1<<m);i++) {
			ll cont=0;
			ll ans=1;
			for (t=0;t<m;t++) {
				if (i&(1<<t)) {
					ans=lcm(ans,a[t]);
					if (ans>n) {
						break;
					}
					cont++;
				}
			}
			if (cont&1) {
				sum+=n/ans;
			} else {
				sum-=n/ans;
			}
		}
		cout<<n-sum<<endl;
	}
	return 0;
} 

K - 组合数问题

组合数 C_n^mCnm​ 表示的是从 nn 个物品中选出 mm 个物品的方案数。举个例子,从 (1, 2, 3)(1,2,3) 三个物品中选择两个物品可以有 (1, 2),(1, 3),(2, 3)(1,2),(1,3),(2,3) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数的一般公式:

\displaystyle C_n^m = \frac{n!}{m!(n-m)!}Cnm​=m!(n−m)!n!​

其中 n! = 1 \times 2 \times \cdots \times nn!=1×2×⋯×n。

小葱想知道如果给定 n, mn,m 和 kk,对于所有的 0 \le i \le n,0≤i≤n, 0 \le j \le min (i, m)0≤j≤min(i,m) 有多少对 (i, j)(i,j) 满足 C_i^jCij​ 是 kk 的倍数。

输入格式

第一行有两个整数 t, kt,k,其中 tt 代表该测试点总共有多少组测试数据,kk 的意义见题目描述。

接下来 tt 行,每行两个整数 n, mn,m,其中 n, mn,m 的意义见题目描述。

输出格式

tt 行,每行一个整数代表所有的 0 \le i \le n, 0 \le j \le min (i, m)0≤i≤n,0≤j≤min(i,m) 中有多少对 (i, j)(i,j) 满足 C_i^jCij​ 是 kk 的倍数。

样例说明

样例1:

在所有可能的情况中,只有 C_2^1=2C21​=2 是 22 的倍数。

Sample Input

1 2
3 3

Sample Output

1

Sample Input 2

2 5
4 5
6 7

思路

这里组合数构成了杨辉三角,构造三角再求解即可

#include <bits/stdc++.h>
using namespace std;
const int Maxn=2010;
int a[Maxn][Maxn],b[Maxn][Maxn];
int main()
{
	int t,k,i,j,n,m;
	cin>>t>>k;
	for (i=1;i<=2002;i++) {
		a[i][i]=1,a[i][0]=1,b[i][0]=0;
		for (j=1;j<i;j++) {
			a[i][j]=(a[i-1][j-1]+a[i-1][j])%k;
			if (a[i][j]==0) {
				b[i][j]=1;
			}
			b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
		}
		b[i][i]=b[i][i-1];
	}
	while (t--) {
		cin>>n>>m;
		cout<<b[n][min(n,m)]<<endl;
	}
	return 0;
}

L - 同余方程

求关于 xx 的同余方程 ax \equiv 1 (\bmod b)ax≡1(modb) 的最小正整数解。

输入格式

输入只有一行,包含两个正整数 a,ba,b ,用一个空格隔开。

输出格式

输出只有一行,包含一个正整数 x_0x0​,即最小正整数解。输入数据保证一定有解。

数据范围

对于 40\%40% 的数据,2≤b≤1,000;

对于 60\%60% 的数据,2≤b≤50,000,000;

对于 100\%100% 的数据,2≤a,b≤2,000,000,000。

Sample Input

3 10

Sample Output

7

思路

这里还是逆元的应用,使用拓展欧几里得算法求解

#include <bits/stdc++.h>
using namespace std;
long long x,y;
void exgcd(long long a,long long b) {
	if (b==0) {
		x=1;
		y=0;
		return;
	}
	exgcd(b,a%b);
	long long temp=y;
	y=x-a/b*y;
	x=temp;
}
int main()
{
	long long a,b;
	cin>>a>>b;
	exgcd(a,b);
	x=(x%b+b)%b;
	cout<<x<<endl;
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值