acm模板小结

文件读入输出:

freopen("peace.in","r",stdin);
freopen("peace.out","w",stdout);

读入整行:

while(getline(cin,s)){
	//存在s串里头
}

python处理大数运算,a(x)为ax

print(eval(input().replace('(','**(')))

速读:

cin的外挂

ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
//用这个代码里就别出现scanf。

整数类型快读快写:【只能整数!】

//速读1
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
//速读2
template<class T>
inline bool scan_d(T &ret){
	char c; 
	int sgn;
	if(c=getchar(),c==EOF) 
	return 0;
	while(c!='-'&&(c<'0'||c>'9')) 
	c=getchar();
	sgn=(c=='-')?-1:1;
	ret=(c=='-')?0:(c-'0');
	while(c=getchar(),c>='0'&&c<='9') 
	ret=ret*10+(c-'0');
	ret*=sgn;
	return 1;
}
//输出1
inline void out(int x) {
	if(x>9) out(x/10);
	putchar(x%10+'0');
}
//输出2
template<class T>
inline void write(T x){
	if(x<0){
		putchar('-');
		x=-1;
	}
	T y=1;
	int len=1;
	for(;y<=x/10;y*=10)len++;
	for(;len;--len,x%=y,y/=10)
	putchar(x/y+48);
}

数论:

质数相关:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
using namespace std;
const int maxn=1e6+6;
int poi;
int factor[maxn];
int num[maxn]; 
void getP_factor(int x){ 
	for(int i=2;i*i<=x;i++){
		if(x%i==0){
			factor[poi]=i;
			int cnt=0;
			while(x%i==0){
				x=x/i;
				cnt++;
			}
			num[poi++]=cnt;
		}
	}
	if(x>1){
		factor[poi]=x;
		num[poi++]=1;
	}
} 
bool isP(int x){//判断质数 
	for(int i=2;i*i<=x;i++){
		if(x%i==0)return false;
	}
	return true;
}
//高效率判断质数 
ll mult(ll a,ll b,ll p){//(a*b)%p
	a=a%p;
	b=b%p;
	ll ans=0,tmp=a;
	while(b){
		if(b&1){
			ans+=tmp;
			if(ans>p)ans-=p;
		}
		tmp<<=1;
		if(tmp>p)tmp-=p;
		b>>=1;
	}
	return ans;
}
ll qpow(ll a,ll n,ll p){//(a^n)%p
	ll ans=1;
	ll tmp=a%p;
	while(n){
		if(n&1)ans=mult(ans,tmp,p);
		tmp=mult(tmp,tmp,p);
		n>>=1;
	}
	return ans;
}
bool check(ll a,ll n,ll x,ll t){
	ll ans=qpow(a,x,n);
	ll last=ans;
	for(ll i=1;i<=t;i++){
		ans=mult(ans,ans,n);
		if(ans==1&&last!=1&&last!=n-1)return true;
		last=ans;
	}
	return ans!=1; 
}
bool Miller_Rabin(ll n){//n是否为质数 
	if(n<2)return false;
	if(n==2)return true;
	if((n&1)==0)return false;
	ll x=n-1;
	ll cnt=0;
	while((x&1)==0){
		x>>=1;
		cnt++;
	}
	srand(time(NULL));
	const ll times=8;
	for(ll i=0;i<times;i++){
		ll a=rand()%(n-1)+1;
		if(check(a,n,x,i))return false;
	}
	return true;
}
int P[maxn],vis[maxn];
void getP(){//获取质数表 
	P[0]=0;
	for(int i=2;i<maxn;i++){
		if(!vis[i])P[++P[0]]=i;
		for(int j=1;j<=P[0];j++){
			if(i*P[j]>N)break;
			vis[i*P[j]]=1;
		}
	}	
}
void init(){//
    for(int i=2;i<N;i++){
        if(!tag[i]){//i 是素数 
            for(int j=i;j<N;j+=i){//j是N内所有i的倍数 
                if(vis[j/i]==i) tmp[j]=tmp[j/i]+1;
                else tmp[j]=1;
                vis[j]=i;
                sum[j]+=tmp[j];
                tag[j]=1;//非素数 
            }
        }
    }
}
//sum[i]:i的质因子总个数【可重复】 
//vis[i]:i的一个质因子  
//vis[k*i]=i,--->j==k*i*i--->vis[k*i*i/i]=vis[k*i]==i
//tmp[j]:在该层循环中,是数i中分解质因子后j的次数
//sum[j]:数j所有质因子的次数和 


//****************************************************************
// Miller_Rabin 算法进行素数测试
//速度快,而且可以判断 <2^63的数
//****************************************************************
const int S=20;//随机算法判定次数,S越大,判错概率越小
//计算 (a*b)%c.   a,b都是ll的数,直接相乘可能溢出的
//  a,b,c <2^63
ll mult_mod(ll a,ll b,ll c)
{
    a%=c;
    b%=c;
    ll ret=0;
    while(b)
    {
        if(b&1){ret+=a;ret%=c;}
        a<<=1;
        if(a>=c)a%=c;
        b>>=1;
    }
    return ret;
}
//计算  x^n %c
ll pow_mod(ll x,ll n,ll mod)//x^n%c
{
    if(n==1)return x%mod;
    x%=mod;
    ll tmp=x;
    ll ret=1;
    while(n)
    {
        if(n&1) ret=mult_mod(ret,tmp,mod);
        tmp=mult_mod(tmp,tmp,mod);
        n>>=1;
    }
    return ret;
}
//以a为基,n-1=x*2^t      a^(n-1)=1(mod n)  验证n是不是合数
//一定是合数返回true,不一定返回false
bool check(ll a,ll n,ll x,ll t)
{
    ll ret=pow_mod(a,x,n);
    ll last=ret;
    for(int i=1;i<=t;i++)
    {
        ret=mult_mod(ret,ret,n);
        if(ret==1&&last!=1&&last!=n-1) return true;//合数
        last=ret;
    }
    if(ret!=1) return true;
    return false;
}
// Miller_Rabin()算法素数判定
//是素数返回true.(可能是伪素数,但概率极小)
//合数返回false;
bool Miller_Rabin(ll n)
{
    if(n<2)return false;
    if(n==2)return true;
    if((n&1)==0) return false;//偶数
    ll x=n-1;
    ll t=0;
    while((x&1)==0){x>>=1;t++;}
    for(int i=0;i<S;i++)
    {
        ll a=rand()%(n-1)+1;//rand()需要stdlib.h头文件
        if(check(a,n,x,t))
            return false;//合数
    }
    return true;
}
//************************************************
//pollard_rho 算法进行质因数分解
//************************************************
ll factor[100];//质因数分解结果(刚返回时是无序的)
int tol;//质因数的个数。数组小标从0开始
ll gcd(ll a,ll b)
{
    if(a==0)return 1;//???????
    if(a<0) return gcd(-a,b);
    while(b)
    {
        ll t=a%b;
        a=b;
        b=t;
    }
    return a;
}
ll Pollard_rho(ll x,ll c)
{
    ll i=1,k=2;
    ll x0=rand()%x;
    ll y=x0;
    while(1)
    {
        i++;
        x0=(mult_mod(x0,x0,x)+c)%x;
        ll d=gcd(y-x0,x);
        if(d!=1&&d!=x) return d;
        if(y==x0) return x;
        if(i==k){y=x0;k+=k;}
    }
}
//对n进行素因子分解
void findfac(ll n)
{
    if(Miller_Rabin(n))//素数
    {
        factor[tol++]=n;//此处可以用map储存因数个数例如map[n]++;
        return;
    }
    ll p=n;
    while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);
    findfac(p);
    findfac(n/p);
}

扩展欧几里得:

1.求二元一次方程的解求 x * a + y * b = c 的解:

先求xa+yb=gcd(a,b)的特解(x0,y0),若c%gcd(a,b)不为0则无解,否则,令k=c/gcd(a,b),则解为(x0k,y0k)。

//求解x*a+y*b==gcd(a,b)中的一个特解(x0,y0) 
int exgcd(ll a,ll b,ll &x,ll &y){
	ll d=a;
	if(b)d=exgcd(b,a%b,y,x),y-=x*(a/b);
	else x=1,y=0;
	return d;//最大公因数 
} 

2.求逆元:

(A / B) %m = (A * C) %m,则C为B模m意义下的逆元。
也即:B*C = 1 (Mod m)。

费马小定理:

条件:B与m互质,m为质数,B为正整数。
Bm-1恒等于1(Mod m)【Bm-1%m=1】,于是:B* Bm-2%m=1,所以C=Bm-2

ll qpow(ll a,ll n,ll mod){
	ll ans=1;
	while(n){
		if(n&1)ans=ans*a%mod;
		a=a*a%mod;
		n=n>>1;
	}
	return ans;
}
ll inv(ll x, ll mod){
	return qpow(x,mod-2);
}
扩展欧几里得:

B* C=1(Mod m)等价于 C * B+ k* m = 1【k是正、负整数】
条件:B与m互质。

int exgcd(ll a,ll b,ll &x,ll &y){
	ll d=a;
	if(b)d=exgcd(b,a%b,y,x),y-=x*(a/b);
	else x=1,y=0;
	return d;//最大公因数 
} 
ll inv(ll x, ll mod){
	ll a,b;
	ll t=exgcd(x,mod,a,b);
	if(t==1)return (a+mod)%mod;
	return -1
}

矩阵运算:

const int N=2;//
struct Node{
	int a[N][N];
	Node(){};
};
Node mul(Node A,Node B){//矩阵乘法 A*B 
	Node ans;
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			ans.a[i][j]=0;
			for(int k=0;k<N;k++){
				ans.a[i][j]+=(A.a[i][k]*B.a[k][j]);
			}
		}
	}
	return ans;
}
Node one(){//获取单位矩阵 
	Node ans;
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			ans.a[i][j]=0;
		}
	}
	for(int i=0;i<N;i++)ans.a[i][i]=1;
	return ans;
}

void putMat(Node A){//打印矩阵 
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++)
		printf("%d ",A.a[i][j]);
		printf("\n");
	}
	cout<<"\n";
}
Node Matqpow(Node A,int n){//矩阵快速幂 //方阵 
	Node ans=one();
	while(n){
		if(n&1)ans=mul(ans,A);
		A=mul(A,A);
		n=n/2;
	}
	return ans;
}

矩阵快速幂可求斐波那契第n项:
F0=F1=0,求Fn。
在这里插入图片描述

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<cmath>
#include<cstring>
#include<string>
using namespace std;
#define ll long long
const int maxn=1e6+6;
const int mod=1e9+7;
const int N=2;
struct Node{
	int a[N][N];
	Node(){};
};
Node mul(Node A,Node B){ 
	Node ans;
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			ans.a[i][j]=0;
			for(int k=0;k<N;k++){
				ans.a[i][j]=(ans.a[i][j]+((ll)A.a[i][k]*B.a[k][j])%mod)%mod;
			}
		}
	}
	return ans;
}
Node one(){
	Node ans;
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			ans.a[i][j]=0;
		}
	}
	for(int i=0;i<N;i++)ans.a[i][i]=1;
	return ans;
}

void putMat(Node A){ 
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++)
		printf("%d ",A.a[i][j]);
		printf("\n");
	}
	cout<<"\n";
}
Node Matqpow(Node A,int n){ 
	Node ans=one();
	while(n){
		if(n&1)ans=mul(ans,A);
		A=mul(A,A);
		n=n/2;
	}
	return ans;
}
Node getZ(){
	Node ans;
	for(int i=0;i<2;i++){
		for(int j=0;j<2;j++){
			ans.a[i][j]=1;
		}
	}
	ans.a[0][0]=0;
	return ans;
}
int F[maxn];
int main(){
	int n;
	cin>>n;
	F[1]=F[2]=1;//求斐波那契第n个F[n] 
	Node a=getZ(); 
	Node ans=Matqpow(a,n-2);
	int f_n=ans.a[1][0]*F[1]+ans.a[1][1]*F[2];
	int f_n_1=ans.a[0][0]*F[1]+ans.a[0][1]*F[2];
	f_n=f_n%mod;
	f_n_1=f_n_1%mod;
	
//====================================================	
	cout<<"F[n]= "<<f_n<<"   F[n-1]= "<<f_n_1<<"\n";
//check...
	for(int i=3;i<=n;i++)F[i]=(F[i-1]+F[i-2])%mod;
	cout<<"F[n]= "<<F[n]<<"   F[n-1]= "<<F[n-1]<<"\n";
}

一些其他的递推式的快速解法(利用矩阵):https://www.cnblogs.com/frog112111/archive/2013/05/19/3087648.html
在这里插入图片描述上图取自https://blog.csdn.net/red_red_red/article/details/90208713


几何向量【不完整】:

#include "bits/stdc++.h"
using namespace std;
const double eps=1e-6;
const double PI=acos(-1.0);
#define ll long long
template<class T>
bool scan(T &ret){//快读 
	char c;
	int sgn;
	if(c=getchar(),c==EOF)return 0;
	while(c!='-'&&(c<'0'&&c>'9'))c=getchar();
	sgn=(c=='-')?-1:1;
	ret=(c=='-')?0:(c-'0');
	while(c=getchar(),c>='0'&&c<='9')ret=ret*10+(c-'0');
	ret*=sgn;
	return 1;
}
template<class T>
T gcd(T a,T b){
	return b==0?a:gcd(b,a%b);
}
template<class T>
T qpow(T a,T b,T mod){
	T ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
struct Point{
	ll x,y;
	Point(ll x_=0,ll y_=0):x(x_),y(y_){};
	Point operator +(const Point &b)const{return Point(x+b.x,y+b.y);};
	Point operator -(const Point &b)const{return Point(x-b.x,y-b.y);};
	Point operator *(ll t)const{return Point(x*t,y*t);};//数乘
	Point operator /(ll t)const{return Point(x/t,y/t);};
	int operator ==(const Point &b)const{return x==b.x&&y==b.y;};
	double getat2(){return atan2(y,x);};//
	void input(){scanf("%lld%lld",&x,&y);};
	void print(){printf("%lld %lld\n",x,y);}; 
};
ll cross(Point a,Point b){return a.x*b.y-a.y*b.x;};//叉积
int sgn(ll x){//判断正负
	if(x>0)return 1;
	else if (x<0)return -1;
	return 0;
}
bool sameway(Point v1,Point v2){//向量指向是否相同 
	if(sgn(v1.x)==sgn(v2.x)&&sgn(v1.y)==sgn(v2.y))return true;//同向
	return false;
}
int clockwise(Point v1,Point v2){//通过叉积判断v1与v2的相对位置,顺时针方向或者逆时针方向 
	ll t=cross(v1,v2);
	if(t>0)return 1;
	else if(t<0)return -1;
	return 0;
} 
bool cmp(Point a,Point b){//
	return cross(a,b)>0;
} 
int main(){
	;
}
Point waixin(Point a,Point b,Point c){//返回三点圆心
//前提:三点不共线 ab与ac叉积为0即三点共线!
    double a1 = b.x - a.x, b1 = b.y - a.y, c1 = (a1*a1 + b1*b1)/2;
    double a2 = c.x - a.x, b2 = c.y - a.y, c2 = (a2*a2 + b2*b2)/2;
    double d = a1*b2 - a2*b1;
    return Point(a.x + (c1*b2 - c2*b1)/d, a.y + (a1*c2 -a2*c1)/d);
}
int sameLine(Point a,Point b,Point c){
    Point ab=a-b;
    Point ac=a-c;
    return fabs(ab^ac)<=eps;
}
int main(){
    Point a(1,2);
    Point B(2,4);
    Point c(3,6);
    Point ans;
    if(!sameLine(a,B,c)){
        ans=waixin(a,B,c);
        cout<<ans.x<<" "<<ans.y<<"\n";
    }else cout<<"NULL\n";
}



//点积为0:直线或线段垂直
//叉积为0:直线或线段平行

//从夹角小于等于180度来看[顺时针]:
//a^b<0,先a后b
//a^b>0,先b后a
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps = 1e-8;
const double PI = acos(-1.0);
int sgn(double x){
	if(fabs(x) < eps)return 0;
	if(x < 0)return -1;
	else return 1;
}
struct Point{
	double x,y;
	Point(){}
	Point(double _x,double _y){
		x = _x;
		y = _y;
	}
	Point operator -(const Point &b)const{
		return Point(x - b.x,y - b.y);
	}

	//叉积
	double operator ^(const Point &b)const{
		return x*b.y - y*b.x;
	}

	//点积
	double operator *(const Point &b)const{
		return x*b.x + y*b.y;
	}

	bool operator <(const Point& o)const{
        if(fabs(x-o.x)>eps)return x<o.x;
        return o.y-y>eps;
    }
    bool operator ==(const Point& o)const{
        return (fabs(x-o.x)<=eps&&fabs(y-o.y)<=eps);
    }

	//绕原点旋转角度B(弧度值),后x,y的变化
	void transXY(double B){
		double tx = x,ty = y;
		x = tx*cos(B) - ty*sin(B);
		y = tx*sin(B) + ty*cos(B);
	}
};

//1.2 Line 定义
struct Line{
	Point s,e;
	Line(){}
	Line(Point _s,Point _e){
		s = _s;e = _e;
	}

	//两直线相交求交点
	//第一个值为0表示直线重合,为1表示平行,为0表示重合,为2是相交
	//只有第一个值为2时,交点才有意义
	pair<int,Point> operator &(const Line &b)const{
		Point res = s;
		if(sgn((s-e)^(b.s-b.e)) == 0){//直线与Line b所在直线叉积为0-->平行
			if(sgn((s-b.e)^(b.s-b.e)) == 0)
			return make_pair(0,res);//重合
			else return make_pair(1,res);//平行
		}
		double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
		res.x += (e.x-s.x)*t;
		res.y += (e.y-s.y)*t;
		return make_pair(2,res);
	}
	//pair<int,Point> tmp=L1&Lb;
	//if(tmp.first==2)......
};

//中点
Point getMid(Point a,Point b){
	return Point((a.x+b.x)/2,(a.y+b.y)/2);
}

//1.3 两点间距离
//*两点间距离
double dist(Point a,Point b){
	return sqrt((a-b)*(a-b));
}

//1.4 判断:线段相交
//*判断线段相交
bool inter(Line l1,Line l2){
	return
		max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
		max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
		max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
		max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
		sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0 &&
		sgn((l1.s-l2.e)^(l2.s-l2.e))*sgn((l1.e-l2.e)^(l2.s-l2.e)) <= 0;
}

//1.5 判断:直线和线段相交
//判断直线和线段相交
bool Seg_inter_line(Line l1,Line l2){//判断直线l1和线段l2是否相交
	return sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0;
}

//1.6 点到直线距离
//点到直线距离
//返回为result,是该点到直线最近的点
//求距离的话只需要令该点与该返回点用两点距离公式
Point PointToLine(Point P,Line L){
	Point result;
	double t = ((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));//*为点积
	result.x = L.s.x + (L.e.x-L.s.x)*t;
	result.y = L.s.y + (L.e.y-L.s.y)*t;
	return result;
}

//1.7 点到线段距离
//点到线段的距离
//返回点到线段最近的点
Point NearestPointToLineSeg(Point P,Line L){
	Point result;
	double t = ((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));//*为点积
	if(t >= 0 && t <= 1){//垂直距离
		result.x = L.s.x + (L.e.x - L.s.x)*t;
		result.y = L.s.y + (L.e.y - L.s.y)*t;
	}
	else{
		if(dist(P,L.s) < dist(P,L.e))
		result = L.s;
		else result = L.e;
	}
	return result;
}

//1.8 计算多边形面积
//计算多边形面积
//点的编号从0~n-1,按顺序给出
double CalcArea(Point p[],int n){
	double res = 0;
	for(int i = 0;i < n;i++)
	res += (p[i]^p[(i+1)%n])/2;
	return fabs(res);
}

//1.9 判断点在线段上
//*判断点在线段上
bool OnSeg(Point P,Line L){
	return
	sgn((L.s-P)^(L.e-P)) == 0 &&//重合or平行
	sgn((P.x - L.s.x) * (P.x - L.e.x)) <= 0 &&
	sgn((P.y - L.s.y) * (P.y - L.e.y)) <= 0;//P介于[s,e]
}

//1.10 判断点在凸多边形内
//*判断点在凸多边形内
//点形成一个凸包,而且按逆时针排序(如果是顺时针把里面的<0改为>0)
//点的编号:0~n-1
//返回值:
//-1:点在凸多边形外
//0:点在凸多边形边界上
//1:点在凸多边形内
int inConvexPoly(Point a,Point p[],int n){
	for(int i = 0;i < n;i++){
		if(sgn((p[i]-a)^(p[(i+1)%n]-a)) < 0)return -1;
		else if(OnSeg(a,Line(p[i],p[(i+1)%n])))return 0;
	}
	return 1;
}

//1.11 判断点在任意多边形内
//*判断点在任意多边形内
//射线法,poly[]的顶点数要大于等于3,点的编号0~n-1
//返回值
//-1:点在凸多边形外
//0:点在凸多边形边界上
//1:点在凸多边形内
int inPoly(Point p,Point poly[],int n){
	int cnt;
	Line ray,side;
	cnt = 0;
	ray.s = p;
	ray.e.y = p.y;
	ray.e.x = -100000000000.0;//-INF,注意取值防止越界
	for(int i = 0;i < n;i++){
		side.s = poly[i];
		side.e = poly[(i+1)%n];
		if(OnSeg(p,side))return 0;
		if(sgn(side.s.y - side.e.y) == 0)continue;//如果平行轴则不考虑
		if(OnSeg(side.s,ray))
			if(sgn(side.s.y - side.e.y) > 0)
			cnt++;
			else if(OnSeg(side.e,ray)){
				if(sgn(side.e.y - side.s.y) > 0)
				cnt++;
			}
		else if(inter(ray,side))
		cnt++;
	}
	if(cnt % 2 == 1)return 1;
	else return -1;
}

//1.12 判断凸多边形
//判断凸多边形
//允许共线边
//点可以是顺时针给出也可以是逆时针给出
//点的编号1~n-1
bool isconvex(Point poly[],int n){
	bool s[3];
	memset(s,false,sizeof(s));
	for(int i = 0;i < n;i++){
		s[sgn( (poly[(i+1)%n]-poly[i])^(poly[(i+2)%n]-poly[i]) )+1] = true;
		if(s[0] && s[2])return false;
	}
	return true;
}


大数:

ps:以下模板输入的数字默认无前导0!

适用于有取模的大数乘法:

//大数乘法【数据范围在long long内的,或者说是需要取模,否则应用数组模拟乘法】
ll mul(ll a,ll b,ll n)//大数乘法,直接相乘会爆int64,需要逐位相乘
{
    ll s=0;//a*b%n
    while(b){
        if(b&1)s=(s+a)%n;
        a=(a*2)%n;
        b=b>>1;
    }
    return s;
}
ll pow_mod(ll a,ll b,ll n){//修改后的求次方,避免了爆int64
    a=a%n;
    ll s=1;//a^b %n
    while(b){
        if(b&1)s=mul(s,a,n);//s=s*a%n
        a=mul(a,a,n);//a=a*a%n
        b=b>>1;
    }
    return s;
} 

大数运算【无大数除以大数】:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
/*
* 完全大数模板
* 输出cin>>a
* 输出a.print();
* 注意这个输入不能自动去掉前导0的,可以先读入到char数组,去掉前导0,再用构造函数。
*
* BUT 没有大数除大数的运算 
*/
#define MAXN 9999
#define MAXSIZE 1010
#define DLEN 4
/*平时 DLEN==1,MAXN==9*/
class BigNum{
private:
	int a[500]; //可以控制大数的位数 数字182存为281 
	int len;
public:
	BigNum(){len=1;memset(a,0,sizeof(a));} //构造函数
	BigNum(const int); //将一个int类型的变量转化成大数
	BigNum(const char*); //将一个字符串类型的变量转化为大数
	BigNum(const BigNum &); //拷贝构造函数
	BigNum &operator=(const BigNum &); //重载赋值运算符,大数之间进行赋值运算
	friend istream& operator>>(istream&,BigNum&); //重载输入运算符
	friend ostream& operator<<(ostream&,BigNum&); //重载输出运算符

	BigNum operator+(const BigNum &)const; //重载加法运算符,两个大数之间的相加运算
	BigNum operator-(const BigNum &)const; //重载减法运算符,两个大数之间的相减运算
	BigNum operator*(const BigNum &)const; //重载乘法运算符,两个大数之间的相乘运算
	BigNum operator/(const int &)const; //重载除法运算符,大数对一个整数进行相除
	BigNum operator^(const int &)const; //大数的n次方运算
	int operator%(const int &)const; //大数对一个int类型的变量进行取模运算
	bool operator>(const BigNum &T)const; //大数和另一个大数的大小比较
	bool operator>(const int &t)const; //大数和一个int类型的变量的大小比较
	bool operator==(const BigNum &)const;
	void print(); //输出大数
};
BigNum::BigNum(const int b){//将一个int类型的变量转化为大数
	int c,d=b;
	len=0;
	memset(a,0,sizeof(a));
	while(d>MAXN){
		c=d-(d/(MAXN+1))*(MAXN+1);//相当于%10000 
		d=d/(MAXN+1);// /10000 
		a[len++]=c;
	}
	a[len++]=d;
}
BigNum::BigNum(const char *s){//将一个字符串类型的变量转化为大数
	int t,k,index,L,i;
	memset(a,0,sizeof(a));
	L=strlen(s);
	len=L/DLEN;
	if(L%DLEN)len++;
	index=0;
	for(i=L-1;i>=0;i-=DLEN){
		t=0;
		k=i-DLEN+1;//s[k,i]->a[index] 
		if(k<0)k=0;
		for(int j=k;j<=i;j++)
		t=t*10+s[j]-'0';
		a[index++]=t;
	}
}
BigNum::BigNum(const BigNum &T):len(T.len){//拷贝构造函数
	int i;
	memset(a,0,sizeof(a));
	for(i=0;i<len;i++)
	a[i]=T.a[i];
}
BigNum& BigNum::operator=(const BigNum &n){//重载赋值运算符,大数之间赋值运算
	int i;
	len=n.len;
	memset(a,0,sizeof(a));
	for(i=0;i<len;i++)
	a[i]=n.a[i];
	return *this;
}
istream& operator>>(istream &in,BigNum &b){
	char ch[MAXSIZE*4];
	int i=-1;
	in>>ch;
	int L=strlen(ch);
	int count=0,sum=0;
	for(i=L-1;i>=0;){
		sum=0;
		int t=1;
		for(int j=0;j<4&&i>=0;j++,i--,t*=10){
			sum+=(ch[i]-'0')*t;
		}
		b.a[count]=sum;
		count++;
	}
	b.len=count++;
	return in;
}
ostream& operator<<(ostream& out,BigNum& b){//重载输出运算符
	int i;
	cout<<b.a[b.len-1];
	for(i=b.len-2;i>=0;i--){
	printf("%04d",b.a[i]);
	}
	return out;
}
BigNum BigNum::operator+(const BigNum &T)const{ //两个大数之间的相加运算
	BigNum t(*this);
	int i,big;
	big=T.len>len?T.len:len;
	for(i=0;i<big;i++){
		t.a[i]+=T.a[i];
		if(t.a[i]>MAXN){
			t.a[i+1]++;
			t.a[i]-=MAXN+1;
		}
	}
	if(t.a[big]!=0)
	t.len=big+1;
	else t.len=big;
	return t;
}
BigNum BigNum::operator-(const BigNum &T)const{ //两个大数之间的相减运算
	int i,j,big;
	bool flag;
	BigNum t1,t2;
	if(*this>T){
		t1=*this;
		t2=T;
		flag=0;
	}
	else{
		t1=T;
		t2=*this;
		flag=1;
	}
	big=t1.len;
	for(i=0;i<big;i++){
		if(t1.a[i]<t2.a[i]){
			j=i+1;
			while(t1.a[j]==0)j++;
			t1.a[j--]--;
			while(j>i)t1.a[j--]+=MAXN;
			t1.a[i]+=MAXN+1-t2.a[i];
		}
		else t1.a[i]-=t2.a[i];
	}
	t1.len=big;
	while(t1.a[len-1]==0 && t1.len>1){
		t1.len--;
		big--;
	}
	if(flag)t1.a[big-1]=0-t1.a[big-1];
	return t1;
}
BigNum BigNum::operator*(const BigNum &T)const{ //两个大数之间的相乘
	BigNum ret;
	int i,j,up;
	int temp,temp1;
	for(i=0;i<len;i++){
		up=0;
		for(j=0;j<T.len;j++){
			temp=a[i]*T.a[j]+ret.a[i+j]+up;
			if(temp>MAXN){
				temp1=temp-temp/(MAXN+1)*(MAXN+1);
				up=temp/(MAXN+1);
				ret.a[i+j]=temp1;
			}
			else{
				up=0;
				ret.a[i+j]=temp;
			}
		}
		if(up!=0)
		ret.a[i+j]=up;
	}
	ret.len=i+j;
	while(ret.a[ret.len-1]==0 && ret.len>1)ret.len--;
	return ret;
}
BigNum BigNum::operator/(const int &b)const{ //大数对一个整数进行相除运算
	BigNum ret;
	int i,down=0;
	for(i=len-1;i>=0;i--){
		ret.a[i]=(a[i]+down*(MAXN+1))/b;
		down=a[i]+down*(MAXN+1)-ret.a[i]*b;
	}
	ret.len=len;
	while(ret.a[ret.len-1]==0 && ret.len>1)ret.len--;
	return ret;
}

int BigNum::operator%(const int &b)const{ //大数对一个 int类型的变量进行取模
	int i,d=0;
	for(i=len-1;i>=0;i--)
	d=((d*(MAXN+1))%b+a[i])%b;
	return d;
}
BigNum BigNum::operator^(const int &n)const{ //大数的n次方运算
	BigNum t,ret(1);
	int i;
	if(n<0)exit(-1);
	if(n==0)return 1;
	if(n==1)return *this;
	int m=n;
	while(m>1){
		t=*this;
		for(i=1;(i<<1)<=m;i<<=1)t=t*t;
		m-=i;
		ret=ret*t;
		if(m==1)ret=ret*(*this);
	}
	return ret;
}
bool BigNum::operator>(const BigNum &T)const{ //大数和另一个大数的大小比较
	int ln;
	if(len>T.len)return true;
	else if(len==T.len){
		ln=len-1;
		while(a[ln]==T.a[ln]&&ln>=0)ln--;
		if(ln>=0 && a[ln]>T.a[ln])
		return true;
		else
		return false;
	}
	else
	return false;
}
bool BigNum::operator>(const int &t)const{ //大数和一个int类型的变量的大小比较
	BigNum b(t);
	return *this>b;
}
bool BigNum::operator==(const BigNum &T)const{
	if(len!=T.len)return false;
	for(int i=0;i<len;i++){
		if(a[i]!=T.a[i])return false;
	}
	return true;
}

void BigNum::print(){ //输出大数
	int i;
	printf("%d",a[len-1]);
	for(i=len-2;i>=0;i--)
	printf("%04d",a[i]);
	printf("\n");
}
BigNum f[110];//卡特兰数
int main(){
//	cin>>f[1];
//	f[1].print();
	f[0]=BigNum(1);
	f[1]=BigNum(2);
	cout<<(f[0]>f[1]);
	for(int i=1;i<=100;i++)
	f[i]=f[i-1]*(4*i-2)/(i+1);//卡特兰数递推式
	int n;
	while(scanf("%d",&n)==1){
		if(n==-1)break;
		f[n].print();
	}
	return 0;
}

大数除以大数,结果是整数,有余数:

代码是看了这篇博客的:https://www.cnblogs.com/Hankercat/p/10331701.html

#include<iostream>
#include<cstring>
using namespace std;
void init(int a[]){
    string s;
    cin>>s;
    a[0]=s.length();
    for(int i=1;i<=a[0];i++)
        a[i]=s[a[0]-i]-'0';
}
void print(int a[]){
    if(a[0]==0){
        cout<<0<<endl;
        return;
    }
    for(int i=a[0];i>0;i--)
        cout<<a[i];
    cout<<endl;
    return;
}
int cmp(int a[],int b[]){
	if(a[0]>b[0])return 1;
	else if(a[0]<b[0])return -1;
	for(int i=a[0];i>=1;i--){
		if(a[i]>b[i])return 1;
		else if(a[i]<b[i])return -1;
	}
	return 0;
}
void sub_for_div(int a[],int b[]){
	int flag=cmp(a,b);
	if(flag==0){
		a[0]=0;
		return;
	} 
	if(flag==-1)return; 
	for(int i=1;i<=a[0];i++){
		if(a[i]<b[i]){
			a[i+1]--;
			a[i]=a[i]+10;
		}
		a[i]=a[i]-b[i];
	}
	while(a[0]>0&&a[a[0]]==0)a[0]--;
	return;
}
void div(int a[],int b[],int c[]){
	int tmp[506];
	c[0]=a[0]-b[0]+1;
	for(int i=c[0];i>=1;i--){
		memset(tmp,0,sizeof(tmp));
		for(int j=1;j<=b[0];j++)
		tmp[j+i-1]=b[j];
		tmp[0]=b[0]+i-1;
		while(cmp(a,tmp)>=0){
			c[i]++;
			sub_for_div(a,tmp);
		}
	}
	while(c[0]>0&&c[c[0]]==0)c[0]--;
	return;
}
int a[101],b[101],c[101];
int main(){
    init(a);
    init(b);
    div(a,b,c);
    print(c);
    print(a);
}

内有大浮点数除法代码以及大浮点数加减思路>:https://blog.csdn.net/qq_30172585/article/details/50412284

大数除以大数,结果有小数:

(代码取自以上链接)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std; 
int compare(string str1,string str2){   //比较字符串型的“数字”大小,相等返回0,大于返回1,小于返回-1
    if (str1.length() > str2.length()) return 1; //长度长的整数大于长度小的整数
    else if (str1.length() < str2.length()) return -1;
    else    return str1.compare(str2); //若长度相等,则头到尾按位比较
}

string SUB_INT(string str1,string str2);
string ADD_INT(string str1,string str2) {//高精度加法
    int sign = 1; //sign 为符号位
    string str;
    if (str1[0] == '-'){//如果其中一个是负数,那么可以转化成高精度减法;如果都是负数,那么确定下结果为负数,然后擦除负号后相加
        if (str2[0] == '-'){ 
            sign = -1;
            str = ADD_INT(str1.erase(0,1),str2.erase(0,1));
        } 
        else 
        str = SUB_INT(str2,str1.erase(0,1));
    } 
    else{
        if(str2[0] == '-')
        str = SUB_INT(str1,str2.erase(0,1));
        else{ 
            int L1,L2;
            L1 = str1.length();
            L2 = str2.length();
            if (L1 < L2){              //在长度小的前面加0补齐,使得两数对齐 
                for (int i = 1;i <= L2-L1; i++) str1="0"+str1;
            } 
            else
            for (int i = 1;i <= L1-L2; i++) str2="0"+str2;
            
			int int1 = 0,carry = 0; //carry 记录进位
            for (int i = str1.length()-1;i >= 0; i--){
                int1 = (int(str1[i])-'0'+ int(str2[i])-'0'+carry) % 10;
                carry = (int(str1[i])-'0'+ int(str2[i])-'0'+carry) / 10;
                str = char(int1 + '0') + str;
            }
            if (carry != 0) str = char(carry+'0') + str;
        }
    }

    if ((sign==-1)&&(str[0]!='0'))str="-"+str;    //处理符号位
    return str;
}
string SUB_INT(string str1,string str2) {//高精度减法
    int sign=1; //sign 为符号位
    string str;
    int i,j;
    if (str2[0] == '-'){    //减一个负数等于加上它的相反数 
        str = ADD_INT(str1,str2.erase(0,1));
    } 
    else {
        if (str1[0] == '-'){           //负数减去正数 
            sign = -1;
            str = ADD_INT(str1.erase(0,1),str2);
        }
        else{
            int res = compare(str1,str2);
            if (res == 0) return "0";      //两数相等则结果是0 
            if (res < 0){                   //被减数比较小,则符号先确定为负 
                sign = -1;
                string temp = str1;
                str1 = str2;
                str2 = temp;
            }
            int tempint;
            tempint=str1.length()-str2.length();
            for (i=str2.length()-1;i>=0;i--){ 
                if (str1[i+tempint]<str2[i]){   //需要向前借位的情况 
                    j=1;
                    while(1){
                        if (str1[i+tempint-j]=='0'){   //被借的位如果是 0 ,那么继续借位,并把当前的 0 变成 9 
                            str1[i+tempint-j]='9';
                            j++;
                        } 
                        else{
                            str1[i+tempint-j]=char(int(str1[i+tempint-j])-1);  // 被借的位如果不是 0 ,那么减去 1
                            break;
                        }
                    }
                    str=char(str1[i+tempint]-str2[i]+'0'+10)+str;
                } 
                else 
                str=char(str1[i+tempint]-str2[i]+'0')+str;
            }
            for (i=tempint-1;i>=0;i--) str=str1[i]+str;
        }
    }
    //去除结果中多余的前导0
    str.erase(0,str.find_first_not_of('0'));
    if (str.empty()) str="0";
    if ((sign==-1) && (str[0]!='0')) str ="-"+str;
    return str;
}

void TurnInt(string &s1,string &s2){//将s1,s2扩大pow(10,x)次,使得两个数都是整数 
    int after_point1,after_point2;
    int pos1 = s1.find_first_of('.');
    int pos2 = s2.find_first_of('.');
    int L1=s1.length() ;
    int L2=s2.length() ;
    if(pos1 >= 0 && pos1 < L1-1) after_point1 = s1.length() - pos1 -1;
    else after_point1 = 0;
    if(pos2 >= 0 && pos2 < L2-1) after_point2 = s2.length() - pos2 -1;
    else after_point2 = 0;

    int ab = abs(after_point1 - after_point2);
    if(after_point1!=0 && after_point1 < after_point2){
        s1.erase(pos1,1);
        for (int i = 1;i <= ab; i++) s1= s1 + "0";
        s2.erase(pos2,1);
    }
    else if(after_point2!=0 && after_point1 >= after_point2){
        s1.erase(pos1,1);
        for (int i = 1;i <= ab; i++) s2= s2 + "0";
        s2.erase(pos2,1);
    }
    else if(after_point1 == 0){
        s2.erase(pos2,1);
        for (int i = 1;i <= ab; i++) s1= s1 + "0";
    }
    else if(after_point2 == 0){
        s1.erase(pos1,1);
        for (int i = 1;i <= ab; i++) s2= s2 + "0";
    }
} 
string MUL_INT(string str1,string str2) {   //高精度乘法
    int sign = 1;   //sign 为符号位
    string str;
    if (str1[0]=='-'){         //确定结果正负 
        sign *= -1;
        str1 = str1.erase(0,1);
    }
    if (str2[0]=='-'){
        sign *= -1;
        str2 = str2.erase(0,1);
    }
    int L1,L2;
    L1 = str1.length();
    L2 = str2.length();
    for (int i = L2-1;i >= 0;i--){ //模拟手工乘法竖式
        string temps;              //temps存当前str2某一位乘于str1的结果    
        int int_res=0,carry=0,int2=str2[i]-'0';  //carry存进位的数量,int2存str2的某一位 
        if (int2 != 0){
            for (int j = 1;j <= L2-1-i; j++) temps = "0"+temps;   //这里就是上面所说的加上相应位数的 0 的操作 
            for (int j = L1-1;j >= 0;j--){
                int_res = (int2 * (int(str1[j]) - '0') + carry) % 10;
                carry = (int2 *(int(str1[j]) - '0') + carry) / 10;
                temps = char(int_res + '0') + temps;
            }
            if (carry != 0) temps = char(carry +'0') + temps;
        }
        str = ADD_INT(str,temps);         //这里就是上面所说的乘法是基于加法的 
    }
    //去除结果中的前导0
    str.erase(0,str.find_first_not_of('0'));
    if (str.empty()) str = "0";
    if ((sign==-1) && (str[0]!='0')) str = "-" + str;
    return str;
}
string DIVIDE_INT(string str1,string str2) {//高精度浮点数除法。
    string quotient,residue; //商和余数
    int sign1=1,sign2=1;
    if(str2 == "0")  //判断除数是否为0
    return  "ERROR!";

    if(str1[0]=='-'){
        str1  = str1.erase(0,1);
        sign1 *= -1;//商 
        sign2  = -1;//被除数 
    }
    if(str2[0]=='-'){
        str2   = str2.erase(0,1);
        sign1 *= -1;
    }
    if((str1.find_first_of('.')>=0 && str1.find_first_of('.')<=str1.length()-1) || (str2.find_first_of('.')>=0 && str2.find_first_of('.')<=str2.length()-1))
    TurnInt(str1,str2);
    
	str1.erase(0,str1.find_first_not_of('0'));
    str2.erase(0,str2.find_first_not_of('0'));
    int L1,L2;
    L1=str1.length();
    L2=str2.length();
    string tempstr;
    tempstr.append(str1,0,L2-1);
    int i=L2-2;
    int cnt = 0;
    while(i++,tempstr!="0" || i<=L1-1){//模拟手工除法竖式
        if(i>L1-1){
            if(cnt >= 50) break;         //确定保留的最大位数 
            cnt++;
            tempstr=tempstr+"0";              
        } 
        else                                                                                                         
        tempstr=tempstr+str1[i];
        tempstr.erase(0,tempstr.find_first_not_of('0'));
        if (tempstr.empty()) tempstr="0";
        
        for (char ch='9';ch>='0';ch--){ //试商 
            string str;
            str=str+ch;
            if (compare(MUL_INT(str2,str),tempstr)<=0){
                quotient=quotient+ch;
                tempstr =SUB_INT(tempstr,MUL_INT(str2,str));
                break;
            }
        }
    }
    int ql = quotient.length();
    if(cnt > 0) quotient.insert(ql-cnt,".");   
    if(quotient[0] == '.') quotient = "0" + quotient;
    if(quotient[quotient.find_first_not_of('0')] != '.')  quotient.erase(0,quotient.find_first_not_of('0'));  //去除结果中的前导0
    if (quotient.empty()) quotient="0";
    if ((sign1==-1)&&(quotient[0]!='0')) quotient="-"+quotient;
    return quotient;

}
string s1,s2;
int main(){
	cin>>s1>>s2;
	cout<<DIVIDE_INT(s1,s2);
}

精度:

k是数a和数b的差值,用于比较浮点数a,b的大小

const double eps=1e-6;
//k=a-b
int cmp(double k){
	if(k>eps)return 1;//a>b
	else if(k<-eps)return -1;//a<b
	return 0;//a==b
}

组合数等:

综合在一起:

  • fac[i]表示i的阶乘
  • ifac[i]表示i的阶乘模mod下的逆元
  • fpm快速幂
  • comb是组合数C(x,y)
inline int fpm(int x, int y) {
   int r = 1;
   while(y) {
       if(y & 1) r = 1LL * r * x % mod;
       x = 1LL * x * x % mod, y >>= 1;
   }
   return r;
}
inline int perm(int x, int y) { return 1LL * fac[x] * ifac[x - y] % mod; }
inline int comb(int x, int y) { return 1LL * perm(x, y) * ifac[y] % mod; }
 
	fac[0] = 1;
    for(int i = 1; i <= n; ++i) fac[i] = 1LL * i * fac[i - 1] % mod;
    ifac[n] = fpm(fac[n], mod - 2);
    for(int i = n; i; --i) ifac[i - 1] = 1LL * i * ifac[i] % mod;

单纯组合数:

C数值即组合数数组,C [ i ][ j ] 表示从 i 个物品中取 j 个。

int C[40][40];
void init(){
	C[0][0]=1;
	for(int i=1;i<35;i++){
		C[i][0]=C[i-1][0];
		for(int j=1;j<=i;j++){
			C[i][j]=C[i-1][j]+C[i-1][j-1];
		}
	}	
}

dp:

https://blog.csdn.net/qq_42819250/article/details/107948376


数据结构:

线段树:

int T[4*maxn];
void update(int x,int l,int r,int id_,int num){//修改
	if(l==r){
		T[x]=num;
		return;
	}
	int mid=(l+r)>>1;
	if(id_>mid)update(2*x+1,mid+1,r,id_,num);
	else update(2*x,l,mid,id_,num);
	T[x]=__gcd(T[2*x],T[2*x+1]);//维护区间最大公因数
}
int query(int x,int l,int r,int L,int R){//查询[L,R]区间内的数的最大公因数
	if(L<=l&&r<=R)return T[x];
	int mid=(l+r)>>1;
	int ans=0;
	if(L<=mid)ans=__gcd(ans,query(2*x,l,mid,L,R));
	if(R>mid)ans=__gcd(query(ans,2*x+1,mid+1,r,L,R));
	return ans;
}

树状数组:


int lowbit(int x){
    return x&(-x);
}
void updata(int i,int k){//给i加k 
    while(i<=n){
        c[i]+=k;
        i+=lowbit(i);
    }
}
int getsum(int i){//[1,i]间的答案 
    int res = 0;
    while(i>0){
        res+=c[i];
        i-=lowbit(i);
    }
    return res;
}

图论模板:

建图:

一种用vector,另一种(比较快):

int head[maxn],cnt;
struct node{
	int nxt,to,w;
}e[maxn*2];
void add(int a,int b){//a->b连边
	e[++cnt].nxt=head[a];
	e[cnt].to=b;
	e[cnt].w=0;
	head[a]=cnt;
}
void xxx(int u){
	for(int i=head[u];i;i=e[i].nxt){
		int to=e[i].to;
		//......
	}
}

最短路:

这里噢:https://www.cnblogs.com/shuizhidao/p/9365884.html

Floyd:

n点,边存在mp二维数组中【n=500可能不大ok】
多源

 for(int k = 1;k <= n;++k)
     for(int i = 1;i <= n;++i)
         for(int j = 1;j <= n;++j)
         mp[i][j] = min(mp[i][k] + mp[k][j], mp[i][j]);

Dijkstra:

只适用无负权值的单源点最短路~
Nlog2N

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <sstream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <iomanip>
#include <stack>

using namespace std;

typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MAXN = 10005;
const int MOD = 1e9 + 7;

#define MemI(x) memset(x, -1, sizeof(x))
#define Mem0(x) memset(x, 0, sizeof(x))
#define MemM(x) memset(x, 0x3f, sizeof(x))

struct Edge
{
    int to, w, next;
}edge[MAXN << 1];
int head[MAXN], cnt;

int dis[MAXN], n, m;
struct cmp
{
    bool operator () (const int a, const int b)
    {
        return dis[a] > dis[b];
    }
};
priority_queue<int, vector<int>, cmp> q;//令最小的dist[i]出队。感觉是让最小的先开枝散叶..
void Dijkstra(int s)
{
    while(!q.empty())q.pop();
    memset(dis, 0x3f, sizeof(dis));
    dis[s] = 0;
    q.push(s);
    int u;
    //理论上用Dijkstra的题不存在负权边,即每次选出来的边即为最短边,
    //所以每个点不可能被多次松弛,入队最多一次
    //所以那个 vis 应该也是多余的
    while(!q.empty())
    {
        u = q.top();q.pop();
        for(int i = head[u];i != -1;i = edge[i].next)
        {
            if(dis[edge[i].to] > dis[u] + edge[i].w)
            {
                dis[edge[i].to] = dis[u] + edge[i].w;
                q.push(edge[i].to);
            }
        }
    }
}

void Add(int a, int b, int c)
{
    edge[++cnt].to = b;
    edge[cnt].w = c;
    edge[cnt].next = head[a];
    head[a] = cnt;
}

int main()
{
    while(cin >> n >> m)
    {
        MemI(head);
        cnt = 0;
        if(!n && !m)
            break;
        int a, b, c;
        for(int i = 0;i < m;++i)
        {
            cin >>  a >> b >> c;
            Add(a, b, c);
            Add(b, a, c);
        }
        Dijkstra(1);
        cout << dis[n] << endl;
    }
    return 0;
}

SPFA:

SPAF算法是 Belleman-Ford算法的队列优化。
可求权图的单源最短路径
可用来跑差分约束
SPAF算法判断负权环:某点进队超过n次。
负权环:如果存在一个环路径上的权值和是负数,也叫负权回路。
SPFA的算法时间效率不是很稳定,通常还是使用Dijkstra算法。
KE

	//nt:dist
		for(int i=1;i<=n;i++){
		nt[i]=inf; 
		in[i]=0;
		tot[i]=0;	
	}
	queue<int>qu;
	while(!qu.empty())qu.pop();
	qu.push(s);
	in[s]=1;
	nt[s]=0;
	tot[s]=1;
	int flag=0;//是否有负环 
	while(!qu.empty()){
		int u=qu.front();
		qu.pop();
		in[u]=0;
		for(int i=0;i<G[u].size();i++){
			int v=G[u][i].first;
			int w=G[u][i].second;
			if(nt[v]>nt[u]+w){
				nt[v]=nt[u]+w;
				fa[v]=u;
				//nt[v]变了,所以v结点需要在队列中,以便重新更新
				if(in[v]==0){
					tot[v]++;
					in[v]=1;
					qu.push(v);
					if(tot[v]>n){
						flag=1;break;
					}
				}
			}
		}
		if(flag)break;
	}
	if(flag){
		cout<<"IMPOSSIBLE\n";
		return 0;
	} 
	int poi=0;
	int x=t;//nt[t]:从点s出发到t经过的最小节点数
	while(x!=s){
		ans[++poi]=x;//最短路径路径倒着存【1--poi】
		x=fa[x];
	}

对于为什么spfa和dijkstra这么像,一个可以处理负权一个却不可以,应该是因为dijkstra由于只处理非负权,所以每个点入队是只一次的,而spfa可多次。


二分图染色

const int maxn=1e3+6;
int flag=0;
int vis[maxn];
int C[maxn];
vector<int>G[maxn];
void dfs(int x,int c){
	vis[x]=1;
	for(int i=0;i<G[x].size();i++){
		int u=G[x][i];
		if(!vis[u]){
			C[u]=c;
			dfs(u,1^c);
		}
		else if(vis[u]==1&&C[x]==C[u]){
			flag=1;
			return;
		}
	}
} 
int main(){
	int n,x;
	cin>>n;
	for(int i=1;i<=n;i++){
		while(1){
			scanf("%d",&x);
			if(x==0)break;
			G[i].push_back(x);
			G[x].push_back(i);
		}
	}
	flag=0;
	for(int i=1;i<=n;i++){
        if(vis[i]==0){
        	C[i]=0;
            dfs(i,1);
        }
    }
	if(flag==1){
		printf("-1");
		return 0;
	}
	for(int i=1;i<=n;i++)printf("%d",C[i]);
}

二分图匹配

KM求最大匹配【婚配问题】O(n*m)

vector<int>G[maxn];
int vis[maxn],p[maxn];
int dfs(int x){
	for(int i=0;i<G[x].size();i++){
		int v=G[x][i];
		if(vis[v]==0){
			vis[v]=1;
			if(p[v]==0||dfs(p[v])){
				p[v]=x;
				return 1;
			}
		}
	}
	return 0;
}
void init(int n){
	for(int i=1;i<=n;i++){
		vis[i]=p[i]=0;
		G[i].clear(); 
	}
}
void init_(int n){
	for(int i=1;i<=n;i++)vis[i]=0;
}
int main(){
	int T;
	cin>>T;
	while(T--){
		int n,m;
		scanf("%d%d",&n,&m);
		init(n);
		for(int i=1;i<=m;i++){
			int a,b;
			scanf("%d%d",&a,&b);
			G[a].push_back(b);
		}
		//---------------
		int ans=0;
		for(int i=1;i<=n;i++){
			init_(n);
			if(dfs(i)==1)ans++;
		} 
		printf("%d\n",ans);
		//---------------
	}
}

最大流求二分图最大匹配:

Dinic算法的时间复杂度的理论上界是O(N2*M)(N是结点数,M是边数),但实际上Dinic算法比这个理论上界好得多。如果所有边容量均为1,那么时间复杂度是O(min(N0.67,M0.5) * M) ;对于二分图最大匹配这样的特殊图,时间复杂度是O(N0.5 * M)。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<sstream>
#define ll long long
#define pii pair<int,int>
using namespace std;
const int maxn=4e3+6;
const int inf=0x3f3f3f3f;
int head[maxn],nx[maxn],ver[maxn],C[maxn];
int tot;
void add(int u,int v,int c){
    ver[tot]=v;
    nx[tot]=head[u];
    C[tot]=c;
    head[u]=tot++;
}
int dep[maxn];
bool bfs(int s,int t){
    queue<int>qu;
    while(!qu.empty())qu.pop();
    memset(dep,0,sizeof(dep));
    dep[s]=1;
    qu.push(s);
    while(!qu.empty()){
        int x=qu.front();qu.pop();
        for(int i=head[x];i!=-1;i=nx[i]){
            if(C[i]){
                int v=ver[i];
                if(!dep[v]){
                    dep[v]=dep[x]+1;
                    qu.push(v);
                }
            }
        }
    }
    if(dep[t]==0)return 0;
    return 1;
}
int dinic(int s,int t,int u,int flow){
    if(u==t||flow==0)return flow;
    int res=flow;
    for(int i=head[u];i!=-1;i=nx[i]){
        if(C[i]){
            int v=ver[i];
            if(dep[v]==dep[u]+1){
                int k=dinic(s,t,v,min(res,C[i]));
                C[i]-=k;
                C[i^1]+=k;
                res-=k;
                if(res==0)return flow;
            }
        }
    }
    return flow-res;
}
void init(){
    memset(head,-1,sizeof(head));
    tot=0;
}
void work(int k,int n,int m){
    init();
    mp.clear();
    int S,T;
    S=m+n+1;
	T=S+1;
    for(int i=1;i<=n;i++){
        add(S,i,1),add(i,S,0),mp[pii(S,i)]=1;
    }
    for(int i=1;i<=m;i++){
        add(i+n,T,1),add(T,i+n,0),mp[pii(i+n,T)]=1;
    }
    for(int i=1;i<=k;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b+n,1),add(b+n,a,0);
    }
    int ans0=0;
    while(bfs(S,T)){
        int fl=0;
        while((fl=dinic(S,T,S,inf)))ans0=ans0+fl;
    }
    printf("%d\n",ans0);
}
int main(){
    int k,n,m;
    while(scanf("%d",&k)&&k){
        scanf("%d%d",&n,&m);
        work(k,n,m);
    }
}

tarjan:

https://blog.csdn.net/qq_42819250/article/details/109911220

  • Tarjan找桥:x的一个子节点y,dfn[x]<low[y],边就是桥。
  • Tarjan割点:一个顶点u是割点,当且仅当满足(1)或(2)
    (1) u为树根,且u有多于一个子树。
    (2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)。

有题目:https://ac.nowcoder.com/acm/contest/7501/D

无向图的tarjan:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
using namespace std;
#define ll long long
const int maxn=3e5+6;
vector<int>G[maxn];
int dfn[maxn],low[maxn],snum[maxn];
int deep;
void tarjan(int u,int fa){
    dfn[u]=++deep;
    low[u]=deep;
    int sz=G[u].size();
    for(int i=0;i<sz;i++){
        int v=G[u][i];
        if(!dfn[v]){
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])snum[u]++; //u未必就是割点 
        }
        else if(v!=fa)low[u]=min(low[u],dfn[v]);//和有向图的区别
    }
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
		int aa,bb;
		scanf("%d%d",&aa,&bb);
		G[aa].push_back(bb);
		G[bb].push_back(aa);
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(!dfn[i])tarjan(i,i),ans++,snum[i]--;//根节点需要snum[i]-- 
	}
	//snum[i]:去掉i后,增加的联通块数。snum[i]>0时,i才为割点。 
/*
	for(int i=1;i<=n;i++){
		if(i!=1)printf(" ");
		printf("%d",ans+snum[i]);
	} 
*/
}

有向图的tarjan:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+6;
vector<int>G[maxn];
int tot,tp,cnt;
int dfn[maxn],low[maxn],st[maxn],in[maxn],col[maxn];
int v[maxn],val[maxn];
void tarjan(int x){
	dfn[x]=low[x]=++tot;
	st[++tp]=x;
	in[x]=1;
	for(int i=0;i<G[x].size();i++){
		int v=G[x][i];
		if(!dfn[v]){
			tarjan(v);
			low[x]=min(low[x],low[v]);
		}
		else if(in[v])
		low[x]=min(low[x],dfn[v]);
	}
	if(dfn[x]==low[x]){
		cnt++;
		col[x]=cnt;
		val[cnt]=v[x];
		in[x]=0;
		while(st[tp]!=x){
			col[st[tp]]=cnt;
			val[cnt]+=v[st[tp]];
			in[st[tp]]=0;
			tp--;
		}
		tp--;//这里小心!
	}
}

欧拉回路&欧拉通路

所有边必须被访问且只访问一次,最后终点是起点:欧拉回路;终点不是起点:欧拉路。
无向图G具有一条欧拉回路,当且仅当G是连通的,并且所有结点的度数均为偶数。
有向图G具有单向欧拉路,当且仅当它是连通的,而且除两个结点外,每个结点的入度等于出度,但这两个结点中,一个结点的入度比出度大1,另一个结点的入度比出度小1。
有向图G具有一条单向欧拉,当且仅当它是连通的,且每个结点入度等于出度

以下代码前提是欧拉回路或欧拉通路必存在时:

//在有向图中:
void dfs(int u){
	for(int i=0;i<G[u].size();i++){
		int v=G[u][i];
		if(vis[u][v]==0){
			vis[u][v]=1;//若在无向图中,则这里还令需要vis[v][u]=1
			dfs(v);
		}
	}
	poi++;
	ans[poi]=u;
}
int main(){
	int n;
	e_num=0;
	cin>>n;
	for(int i=0;i<n;i++){//建图这里是由n个环,环中有m条边,即将经过m+1个点
		int m;
		scanf("%d",&m);
		e_num+=m;
		int u,p;
		for(int j=0;j<=m;j++){
			scanf("%d",&u);
			if(j!=0)G[p].push_back(u);
			p=u;
		}
	}
	poi=0;
	ok=0;
	dfs(1);
	if(e_num+1!=poi)printf("0\n");//e_num是边数,poi是欧拉回路经过的点数
	else{
		printf("%d\n",e_num);
		for(int i=poi;i!=1;i--){//输出路径是倒序的
			printf("%d ",ans[i]);
		}
		printf("%d",ans[1]);
		printf("\n");
	}
}

拓扑

当且仅当一个有向图为有向无环图(directed acyclic graph,或称DAG)时,才能得到对应于该图的拓扑排序。每一个有向无环图都至少存在一种拓扑排序。
【tarjan可去环】
模板1:【好写好记但是无法判断topo序是否唯一

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set> 
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn=50003;
const int mod=1e9+7;
const int maxx=1e9+2;
vector<int>G[maxn];
int nt[maxn],vis[maxn];
int poi;
void topo(int x){
	vis[x]=1;
	for(int i=0;i<G[x].size();i++){
		int v=G[x][i];
		if(!vis[v])topo(v);
	}
	nt[++poi]=x;
}
int n;
void topo(){
	poi=0;
	for(int i=1;i<=n;i++){
		if(!vis[i])topo(i);
	}
	for(int i=poi;i;i--){//倒序
		int v=nt[i];
		cout<<v<<" ";
	}
	cout<<"\n";
}
int main(){
	int m;
	cin>>n>>m;
	int u,v;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
	}
	topo();
}

模板2:【可判断topo序唯一性】

int in[maxn];//进度 
void topo(){
    queue<int>q; 
    vector<int>ans; 
	for(int i=1;i<=n;i++){
        if(!in[i])q.push(i);
    }
    int flag=1;//唯一否
    while(!q.empty()){
    	if(q.size()>1)flag=0;//!!!该图的topo序不唯一
        int v=q.front(); 
		q.pop();
        ans.push_back(v);
        for(int i=0;i<G[v].size();i++){
            int u=G[v][i];
            if(!in[u])q.push(u);
        }
    }
    for(int i=0;i<n;i++){
        printf("%d\n", ans[i]);
    }
}


网络流

最大流:

以下是一道题的AC代码:一个矩阵只有W、I、N三种字母,问能划分为多少个不重叠的三元组使得按顺序是一个WIN。【只直线型或L型】【注意建图】

int head[maxn],nx[maxn],ver[maxn],C[maxn];
int tot;
void add(int u,int v,int c){
    ver[tot]=v;
    nx[tot]=head[u];
    C[tot]=c;
    head[u]=tot++;
}
int dep[maxn];
bool bfs(int s,int t){
    queue<int>qu;
    while(!qu.empty())qu.pop();
    memset(dep,0,sizeof(dep));
    dep[s]=1;
    qu.push(s);
    while(!qu.empty()){
        int x=qu.front();qu.pop();
        for(int i=head[x];i!=-1;i=nx[i]){
            if(C[i]){
                int v=ver[i];
                if(!dep[v]){
                    dep[v]=dep[x]+1;
                    qu.push(v);
                }
            }
        }
    }
    if(dep[t]==0)return 0;
    return 1;
}
int dinic(int s,int t,int u,int flow){
    if(u==t||flow==0)return flow;
    int res=flow;
    for(int i=head[u];i!=-1;i=nx[i]){
        if(C[i]){
            int v=ver[i];
            if(dep[v]==dep[u]+1){
                int k=dinic(s,t,v,min(res,C[i]));
                C[i]-=k;
                C[i^1]+=k;
                res-=k;
                if(res==0)return flow;
            }
        }
    }
    return flow-res;
}
void init(){
    memset(head,-1,sizeof(head));
    tot=0;
}
void work(int k,int n,int m){
    init();
    mp.clear();
    int S,T;
    S=m+n+1;
	T=S+1;
    for(int i=1;i<=n;i++){
        add(S,i,1),add(i,S,0),mp[pii(S,i)]=1;
    }
    for(int i=1;i<=m;i++){
        add(i+n,T,1),add(T,i+n,0),mp[pii(i+n,T)]=1;
    }
    for(int i=1;i<=k;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b+n,1),add(b+n,a,0);
    }
    int ans0=0;
    while(bfs(S,T)){
        int fl=0;
        while((fl=dinic(S,T,S,inf)))ans0=ans0+fl;
    }
    printf("%d\n",ans0);
}
int main(){
    int k,n,m;
    while(scanf("%d",&k)&&k){
        scanf("%d%d",&n,&m);
        work(k,n,m);
    }
}

最大团:

https://blog.csdn.net/qq_42819250/article/details/106432810

kmp

const int maxn=1e6+6;
int nxt[maxn];
void getNext(char* p,int lp){
	nxt[0]=-1;
	int i,j;
	i=0;j=-1;
	while(i<lp){
		if(j==-1||p[i]==p[j]){
			++i;
			++j;
			nxt[i]=j;
		}
		else j=nxt[j];
	}
}
int kmp(char* t,char* p,int lt,int lp){//在t串中找到p的位置,并返回起始下标
    getNext(p,lp);
	int i=0,j=0;
	while(i<lt&&j<lp){
		if(j==-1||t[i]==p[j]){
    		i++;j++;
		}
		else j=nxt[j];
	}
	if(j==lp)return i-j;
	return -1;
}
char t[maxn],p[maxn];
int main(){
    scanf("%s%s",t,p);
    int lt,lp;
    lt=strlen(t);
    lp=strlen(p);
    cout<<kmp(t,p,lt,lp)<<"\n";
    cout<<kmp(t+5,p,lt-5,lp);
}

字典树:

string s;
vector<string>word;
int node[maxn][30];
int ok[maxn];
int cnt;
void inn(int id){//用单词建字典树
	int p=0;
	string tmp=word[id];
	for(int i=0;i<tmp.size();i++){
		int t=tmp[i]-'a';
		if(tmp[i]>='A'&&tmp[i]<='Z')t=tmp[i]-'A';
		if(node[p][t]==0)
		node[p][t]=++cnt;
		p=node[p][t];
	}
	ok[p]=id+1;
}
int len;
int ans[maxn];
int ansi;
int dfs(int id){//将一个句子划分成若干个出现过的单词 
	if(id>=len){
		for(int i=0;i<ansi;i++){
			cout<<word[ans[i]]<<" ";
		}
		cout<<"\n";
		return 1;
	}
	int p=0;
	for(int i=id;i<len;i++){
		int t=node[p][s[i]-'a'];
		if(t==0)break;//p的后继没有这个字母
		if(ok[t]){
			ans[ansi++]=ok[t]-1;
			if(dfs(i+1))return 1;
			ansi--;
		}
		p=t;
	}
	return 0;
}
int find(string s){//查询某个单词是否出现过 
	int le=s.size();
	int p=0;
	for(int i=0;i<le;i++){
		int c=s[i]-'a';
		if(node[p][c]==0)return 0;
		p=node[p][c];
	}
	return ok[p];//第几个单词 
}
int main(){
	string t;
	int m;
	cin>>m;
	getchar();
	for(int i=0;i<m;i++){
		cin>>t;
		word.push_back(t);
	}
	for(int i=0;i<word.size();i++){
		inn(i);
	}
	cin>>s;
	len=s.size();
	dfs(0);
}

其他:

单调栈:

用于寻找无序数组 a[] 内,对于i,其左边(或右边)与他最接近大于等于(或小于等于)a[i] 的位置。
找小的,单调栈内单调递增:要保证栈顶元素必须是小于a[i]的,是的话,这栈顶元素便是位置i左边第一个小于a[i]的。

以下代码 l[i] 是位置 i 左边第一个小于 a[i] 的位置,r[i] 是位置 i 右边第一个小于 a[i] 的位置

	stack<int>st;
	while(st.size())st.pop();
	st.push(1);
	l[1]=0;
	for(int i=2;i<=n;i++){
		if(st.size()==0||a[st.top()]<=a[i]){
		//st.top()是其左边与他最接近、第一个比小于等于a[i]的.
		//改成找左边最近的且大于等于a[i],需要将"<="改成">=".
			l[i]=st.top();
			st.push(i);
		}else{
			while(st.size()&&a[st.top()]>a[i])
			//改成找左边最近的且大于等于a[i],将">"改成"<".
			st.pop();
			if(st.size()==0)l[i]=0;
			else l[i]=st.top();
			st.push(i);
		}
	}
	while(st.size())st.pop();
	st.push(n);
	r[n]=n+1;
	for(int i=n-1;i>=1;i--){
		if(st.size()==0||a[st.top()]<=a[i]){
			r[i]=st.top();
			st.push(i);
		}else{
			while(st.size()>0&&a[st.top()]>a[i])
			st.pop();
			if(st.size()==0)r[i]=0;
			else r[i]=st.top();
			st.push(i);
		}
	}

再数字右边找大于当前数字的最接近下标:
【多了一个ffa】

int a[maxn],r[maxn];
int n;
int ffa(int ai,int x){
	if(ai<1||ai>n)return -1;
	if(a[ai]!=x)return ai;
	int xi=ai;
	if(a[ai]==x)xi=ffa(r[ai],x);
	return r[ai]=xi;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	stack<int>st;
	while(st.size())st.pop();
	st.push(n);
	r[n]=n+1;
	for(int i=n-1;i>=1;i--){
		if(st.size()==0||a[st.top()]>=a[i]){
			r[i]=st.top();
			st.push(i);
		}else{
			while(st.size()>0&&a[st.top()]<a[i])
			st.pop();
			if(st.size()==0)r[i]=0;
			else r[i]=st.top();
			st.push(i);
		}
	}
	for(int i=1;i<=n;i++){
		int ans=-1;
		ans=ffa(r[i],a[i]);
		if(ans!=-1)ans=a[ans];
		printf("%d ",ans);
	}
} 

经典题目:求最大全1子矩阵
例如:
1 0 1 1
1 1 1 1
1 1 1 0 最大的矩形区域有6个1,所以返回6.
具体是将二维转成一维:
第一行:1 0 1 1
第二行:2 1 2 2
第三行:3 2 1 0
每行中用单调栈对每个位置求左边最近的、小于该位置高度的地方l,同理求出右边的r,对于以位置i高度h为矩形高度的,其规模是h*(r-1-l)


二分:

//最小化答案
	while(l<r){//在[l,r]区间寻找 
		mid=(l+r)>>1;
		if(a[mid]>=x) r=mid;//查找第一个大于等于x的数 
		//if(a[mid]>=x)r=mid;是查找第一个大于x的数 
		else l=mid+1;  
	}
//最大化答案
	while(l<r){
    	int mid=(r+l+1)>>1;
    	if(a[mid]<=x) l=mid;//查找最后一个小于等于x的数 
		else r=mid-1;
	}

在一个左闭右开的有序区间里进行二分查找:
lower_bound() & upper_bound():找不到返回end()
lower_bound():第一个大于等于
upper_bound():第一个大于

	int t=lower_bound(vc.begin(),vc.end(),x)-vc.begin();
	if(t==vc.end()-vc.begin())cout<<"NO\n";//查找失败!
	else cout<<"YES\n";//t是下标(以0始)

三分:

三分逼近函数(double)峰值:

double calc(double x){//一个函数,以x为自变量 
    return -2.0*x*x+8*x+6;
}
double find_(double l,double r){
    double mid, midmid;
    while(l+eps<r){//double的与int的最大不同之处
        mid=(l+r)/2;
        midmid=(mid+r)/2;
        double mid_value=calc(mid);
        double midmid_value=calc(midmid);
        if(mid_value>midmid_value)
        r=midmid;
        else
        l=mid;
    }
    return l;//即为峰值对应的x
}

三分取函数峰值:


int calc(int x){//一个函数,以x为自变量 
    return -2*x*x+8*x+6;
}
int main(){
	int l=0,r=9;
	while(l<r-1){//double的与int的最大不同之处
		int mid=(l+r)/2;
		int mid_r=(mid+r)/2;
		if(calc(mid)<calc(mid_r))l=mid;
		else r=mid_r;
	}
	int ansx=r;
	int ans1=calc(l);
	int ans2=calc(r);
	if(ans1>ans2)ansx=l;
	cout<<max(ans1,ans2)<<" "<<ansx<<"\n";
}
	int a[6]={1,2,7,4,3,1};
	int l=0,r=5;
	while(l<r-1){
		int mid=(l+r)/2;
		int mid_r=(mid+r)/2;
		if(a[mid]<a[mid_r])l=mid;
		else r=mid_r;
	}
	int ans=max(a[l],a[r]);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值