红鲤鱼与绿鲤鱼与驴
今天学习 二分法 and 快速幂
二分查找函数:
在一个升序数组的 begin 和 end 前闭后开区间内进行二分查找
lower_bound (begin,end,val) 返回大于或等于val的第一个元素位置
upper_bound (begin,end,val) 返回大于val的第一个元素位置
如果所有元素都小于 val 则返回 end 的位置
补充:头文件:algorithm
如果要二分的是个小数 需要规定精度
double left,right,mid; while(right - left > eps ){ mid = (right + left) / 2; if( judge(mid) ) left = mid; else right = mid; } return mid;
二分答案
二分枚举答案,由于进行二分,所以复杂度 log(n),比直接 for 的时间更短。
1、适用情况:求最值,可能答案区间有限且单调且大于零,检验答案合理性复杂度低
2、题目标志:最小值最大、最大值最小
3、模板代码:
while (l <= r){ int mid = (l + r) >> 1;//右移 效果相当于除以二 if (judge(mid)){ ans = mid; l = mid + 1; }else r=mid - 1; } return ans ;
4、例题1 SP297 AGGRCOW - Aggressive cows:
https://www.luogu.com.cn/problem/SP297
题意:有n个牛栏,分别位于X1,X2...Xn 选m个放进牛 使得相邻牛之间的最小距离值最大
思路:先对隔间编号从小到大排序,则最大距离不会超过两端的两头牛之间的差值,最小值为0。所以我们可以通过二分枚举最小值来求。假设当前的最小值为x,如果判断出最小差值为x时可以放下m头牛,说明当前的x有点小,就先让x变大再判断;如果放不下,说明当前的x太大了,就先让x变小然后再进行判断。直到求出一个最大的x就是最终的答案。
#include<iostream> #include<algorithm> using namespace std; int n,c; int a[100005]; int judge(int m){ int num=1,cnt=1; for(int i=2;i<=n;i++){ if(a[i]-a[num]>=m){ cnt++; num=i; if(cnt==c) return 1; } } return 0; } int main() { int t; cin>>t; while(t--){ cin>>n>>c; for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+1+n); int l=1,r=a[n]; while(l<r){ int m=(l+r+1)/2; if(judge(m)) l=m; else r=m-1; } cout<<l<<endl; } return 0; }
5、例题2 CF39H Multiplication Table:
https://www.luogu.com.cn/problem/CF448D
题意:有 n * m 乘法表,将 n * m 个数从小到大依次排列,第K个数是多少?
思路:由于数据既大且多,暴力构造乘法表然后排序的做法不可取。正确做法是使用二分查找。由于乘法表有个特殊的公式,即对于一个数X,X除以行数i 设得到的结果(整数表示)为Q,如果Q大于列数M,那么这个数就大于第i行所有的数,如果等于列数,说明这个数是第i行第Q大的数。如果小于列数,说明这个数比第i行前Q个数大。利用这一点查找mid在乘法表中所处的位置,然后判断mid与k的大小关系进而确定左右边界继续二分,直到找到该位置(左等于右)。
#include<iostream> using namespace std; int main() { long long n,m,k; cin>>n>>m>>k; long long l=1,r=n*m; while(l<r){ long long mid=(l+r)/2,num=0; for(long long i=1;i<=n;i++){ long long q=mid/i; if(q>m) num+=m; else num+=q; } if(num>=k) r=mid; else l=mid+1; } cout<<l<<endl; return 0; }
———————————————————— 分 界 线 ————————————————————
快速幂: ( a ^ b ) mod p
int power(int a,int b,int p){ int ans = 1 % p; for(;b;b>>=1){ if( b & 1 ) ans=(long long)ans * a % p; a=(long long)a * a % p; } return ans; }
矩阵快速幂:将快速幂中的底数变为矩阵 线性递推式
模板题:https://www.luogu.com.cn/problem/P3390
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; const int mod = 1e9 + 7; const int MAXN = 105; struct Mat { ll m[MAXN][MAXN]; }ans; Mat Mul(Mat a, Mat b, ll n) {//计算矩阵a乘矩阵b,n为矩阵的大小 Mat c; memset(c.m, 0, sizeof(c.m)); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= n; k++) c.m[i][j] = (c.m[i][j] + (a.m[i][k] * b.m[k][j]) % mod) % mod; return c; } Mat Mul_power(Mat a, ll b, ll n) {//计算a^b,n为矩阵的大小 for (int i = 1; i <= n; i++)//构造单位矩阵 ans.m[i][i] = 1; while (b) { if (b & 1) ans = Mul(ans, a, n); a = Mul(a, a, n); b >>= 1; } return ans; } int main() { ll n; ll k; cin>>n>>k; Mat zls; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>zls.m[i][j]; Mat zlss=Mul_power(zls,k,n); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) cout<<zlss.m[i][j]<<" "; cout<<endl; } return 0; }
自言自语Part:
晚上有时间的话就把习题做了在发一篇 Day 1.5
Good Luck! 今天还算顺利