文件读入输出:
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]);