模运算
模运算是大数运算中的常用操作。如果一个数太大,无法直接输出,或者不需要直接输出,则可以对它取模,缩小数值再输出。取模可以防止溢出,这是常见的操作。
模是英文mod的音译,取模实际上是求余。
取模运算一般要求a和m的符号一致,即都为正数或都为负数。如果正负不同,那么请小心处理。
取模操作的加、减、乘满足分配律,注意此时仍要求a+b、a−b、a×b为正数,如果有负数,请小心处理。
例题1.刷题统计
2022年(第十三届)省赛,lanqiaoOJ题号2098
这题用暴力法很好解,但是只能拿到60的测试数据,差不多对一半吧。
暴力法代码:
#include <iostream>
using namespace std;
int main()
{// 请在此输入您的代码long long a,b,n; //要用long long cin >> a >> b >> n;long long sum = 0,day = 0; //定义做题数和天数while(sum < n){day++;if(day % 7 == 6 || day % 7 == 0) sum+=b;//周六周日else sum+=a;//周一到周五}cout << day;return 0; //暴力法通过60,后面运行超时,这个是意料之中的。
}
放在取模题中,这也是一道取模的简单题,利用取模操作把计算复杂度降为O(1)。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{long long a,b,n;cin >> a >> b >> n;long long sum=5*a+2*b;//一周总数long long day=7*(n/sum);//总数除以一周总数乘以一周7天n=n%sum;//剩余题目long long d[]={a,a,a,a,a,b,b},i;//设立周数组for(i=0;n>0;i++) {//当n=0时,就已经满足大于等于n,这个时候的天数就是答案n-=d[i];day++;}cout << day; return 0;
}
快速幂
int fastPow(int a, int n) { //快速幂 int ans = 1; //用ans返回结果,初始化为1,不能初始化为0while(n) { //把n看成二进制数,逐个处理它的最后一位if(n & 1) ans *= a; //如果n的最后一位是1,则表示这个地方需要参与计算a *= a; //递推:a2 --> a4 --> a8--> a16-->…n >>= 1; //n右移一位,把刚处理过的n的最后一位去掉}return ans; //结果
}
例题1.快速幂
套模板即可,代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; //变量改用较大的long long型
ll fastPow(ll a, ll n, ll mod) {ll ans = 1;a %= mod; //非常重要,防止下面的ans*a越界while(n) {if(n & 1) ans = (ans*a) % mod; //取模a = a*a % mod; //取模n >>= 1;}return ans; //输出结果
}
int main() {ll b,p,k;cin>>b>>p>>k;cout << fastPow(b,p,k);return 0;
}
矩阵乘法
矩阵的加减法很简单,把两个矩阵对应位置的元素进行加减即可得到结果。
矩阵乘法:
for(int i=1; i<=m; i++) //注:i、j、k的先后顺序不重要,因为对于c[][]来说都一样for(int j=1; j<=u; j++)for(int k=1; k<=n; k++)c[i][j] += a[i][k] * b[k][j]);
根据矩阵乘法的定义,可以推出下面两个式子。
例题1.矩阵相乘
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100;
int n,m,k;
int A[N][N],B[N][N],C[N][N];
int multi(int u, int v) {int sum = 0;for (int j=0; j<m; j++) sum += (A[u][j] * B[j][v]);return sum;
}
int main() {cin >> n >> m >> k;for(int i=0; i<n; i++)for(int j=0; j<m; j++) cin >> A[i][j];for(int i=0; i<m; i++)for(int j=0; j<k; j++) cin >> B[i][j];for(int i=0; i<n; i++)for(int j=0; j<k; j++) C[i][j] = multi(i, j);for(int i=0; i<n; i++) {for(int j=0; j<k; j++) cout << C[i][j] << " ";cout << endl;}return 0;
}
GCD和LCM
最大公约数(Greatest Common Divisor,GCD)和最小公倍数(the Least Common Multiple,LCM)。
编程时可以不用自己写GCD代码,而是直接使用库函数。
C++的库函数__gcd()。
__gcd(); //shift + -,输出_ 注意是两个下划线,所以要操作两次shift+-
库函数__gcd()可能会返回负数,见下面的例子。
#include<bits/stdc++.h>
using namespace std;
int main() {cout<<__gcd(15, 81)<< endl; //输出 3cout<<__gcd(0, 44)<< endl; //输出 44cout<<__gcd(0, 0)<< endl; //输出 0cout<<__gcd(-6, -15)<< endl; //输出 -3cout<<__gcd(-17,289)<< endl; //输出 -17cout<<__gcd(17,-289)<< endl; //输出 17return 0;
}
LCM:
gcd(a, b)×lcm(a, b) = a×b,即lcm(a, b) = a×b/gcd(a, b) =a/gcd(a, b) ×b。
例题1.等差数列
解析:
代码:
#include<bits/stdc++.h>
using namespace std;
int a[100000];
int main() {int n;cin>>n;for(int i=0; i<n; i++) cin>>a[i];sort(a,a+n);int d=0;for(int i=1; i<n; i++) d = __gcd(d,a[i]-a[i-1]); //以{2,5,7}为例if(d==0) cout<<n<<endl; //公差为0,直接输出n就行else printf("%d\n",(a[n - 1] - a[0]) / d + 1); return 0;
}