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;
}