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
    评论
1 图论 3 1.1 术语 3 1.2 独立集、覆盖集、支配集之间关系 3 1.3 DFS 4 1.3.1 割顶 6 1.3.2 桥 7 1.3.3 强连通分量 7 1.4 最小点基 7 1.5 拓扑排序 7 1.6 欧拉路 8 1.7 哈密顿路(正确?) 9 1.8 Bellman-ford 9 1.9 差分约束系统(用bellman-ford解) 10 1.10 dag最短路径 10 1.11 二分图匹配 11 1.11.1 匈牙利算法 11 1.11.2 KM算法 12 1.12 网络流 15 1.12.1 最大流 15 1.12.2 上下界的网络的最大流 17 1.12.3 上下界的网络的最小流 17 1.12.4 最小费用最大流 18 1.12.5 上下界的网络的最小费用最小流 21 2 数论 21 2.1 最大公约数gcd 21 2.2 最小公倍数lcm 22 2.3 快速幂取模B^LmodP(O(logb)) 22 2.4 Fermat小定理 22 2.5 Rabin-Miller伪素数测试 22 2.6 Pollard-rho 22 2.7 扩展欧几里德算法extended-gcd 24 2.8 欧拉定理 24 2.9 线性同余方程ax≡b(mod n) 24 2.10 中国剩余定理 25 2.11 Discrete Logging(BL == N (mod P)) 26 2.12 N!最后一个不为0的数字 27 2.13 2^14以内的素数 27 3 数据结构 31 3.1 堆(最小堆) 31 3.1.1 删除最小值元素: 31 3.1.2 插入元素和向上调整: 32 3.1.3 堆的建立 32 3.2 并查集 32 3.3 树状数组 33 3.3.1 LOWBIT 33 3.3.2 修改a[p] 33 3.3.3 前缀和A[1]+…+A[p] 34 3.3.4 一个二维树状数组的程序 34 3.4 线段树 35 3.5 字符串 38 3.5.1 字符串哈希 38 3.5.2 KMP算法 40 4 计算几何 41 4.1 直线交点 41 4.2 判断线段相交 41 4.3 三点外接圆圆心 42 4.4 判断点在多边形内 43 4.5 两圆交面积 43 4.6 最小包围圆 44 4.7 经纬度坐标 46 4.8 凸包 46 5 Problem 48 5.1 RMQ-LCA 48 5.1.1 Range Minimum Query(RMQ) 49 5.1.2 Lowest Common Ancestor (LCA) 53 5.1.3 Reduction from LCA to RMQ 56 5.1.4 From RMQ to LCA 57 5.1.5 An algorithm for the restricted RMQ 60 5.1.6 An AC programme 61 5.2 最长公共子序列LCS 64 5.3 最长上升子序列/最长不下降子序列(LIS) 65 5.3.1 O(n^2) 65 5.3.2 O(nlogn) 66 5.4 Joseph问题 67 5.5 0/1背包问题 68 6 组合数学相关 69 6.1 The Number of the Same BST 69 6.2 排列生成 71 6.3 逆序 72 6.3.1 归并排序求逆序 72 7 数值分析 72 7.1 二分法 72 7.2 迭代法(x=f(x)) 73 7.3 牛顿迭代 74 7.4 数值积分 74 7.5 高斯消元 75 8 其它 77

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值