ACM 模板

常用函数&&数据

strrve() 将字符前后颠倒
strtol() 将字符串转换成长整型数
strtoul() 将字符串转换成无符号长整型数
strtod() 将字符串转换成浮点数
strchr() 查找某字符在字符串中首次出现的位置
strlwr() 将字符串字符全部变成小写
strupr()将字符串字符全部变成大写
string b=a.substr(a,b) 把string a的第a到b个字符赋给b;
next_permutution(); prev_permutation();
floor()向下取整; ceil(); 向上取整;

构造 n的5次方的矩阵系数
1 5 10 10 5 1
1 4 6 4 1
1 3 3 1
1 2 1
1 1
1
#pragma GCC optimize(2) O2优化
#pragma GCC optimize(3,"Ofast","inline") O3优化

一.图论

1.最短路径

1.1dijkstra

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define maxn 100
#define sf scanf
#define pf printf
using namespace std;
int n,m,x,y,z;  //n个城市和m条道路
int mp[maxn][maxn],lowcost[maxn],vis[maxn],pre[maxn];
void dijkstra(int sta,int edd){
    for(int i=0;i<n;i++) lowcost[i]=mp[sta][i];
    vis[sta]=1; lowcost[sta]=0;
    //寻找距离原点最小的点加进集合 (除原点) 
    for(int i=1;i<n;i++){
        int Min=inf;
        int v=-1;
        for(int j=0;j<n;j++){
            if(!vis[j]&&lowcost[j]<Min){
                Min=lowcost[j];
                v=j;
            }
        }

        //如果又一次更新失败退出此次循环 
        if(Min==inf) {
            break;
        }
        vis[v]=1;
        //用新加进来的点松弛
        for(int k=0;k<n;k++){
            if(!vis[k]&&lowcost[v]+mp[v][k]<lowcost[k]){
                lowcost[k]=lowcost[v]+mp[v][k];
                pre[k]=v;
            }
        } 
    }
}

int main(){
    sf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            mp[i][j]=inf;
        }
        mp[i][i]=0;
    }
    for(int i=0;i<m;i++){
        sf("%d%d%d",&x,&y,&z);
        mp[x][y]=mp[y][x]=z;
    }
    dijkstra(0,n-1);

    if(lowcost[n-1]==inf ) cout<<"-1"<<endl;
    else cout<<lowcost[n-1]<<endl;  

    //假设打印起点是0,终点是n-1的路径
    int sta=0,ed=n-1;
    pre[sta]=-1;
    vector<int> ve;
    vector<int>::iterator ite;
    while(pre[ed]!=-1){
        ve.push_back(ed);
        ed=pre[ed];
    } 
    reverse(ve.begin(),ve.end());
    cout<<sta<<" ";
    for(ite=ve.begin();ite!=ve.end();++ite){
        cout<<*ite;
        if(ite!=ve.end()) cout<<" ";
        else cout<<endl;
    }
    return 0;
}

1.2.floyed

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define maxn 100
#define sf scanf
#define pf printf
using namespace std;
int n,m,x,y,z;  //n个城市和m条道路
int mp[maxn][maxn],lowcost[maxn],vis[maxn],pre[maxn],flag=false;

//k是中间结点,i是起点,j是终点 
void floyed(int mp[][maxn]){
    for(int k=0;k<n;k++){
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(mp[i][j]>mp[i][k]+mp[k][j]){
                    mp[i][j]=mp[i][k]+mp[k][j];
                }
            }
        }
    }
}
int main(){
    sf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            mp[i][j]=inf;
        }
        mp[i][i]=0;
    }
    for(int i=0;i<m;i++){
        sf("%d%d%d",&x,&y,&z);
        mp[x][y]=mp[y][x]=z;
    }
    floyed(mp);
    //可以判断是否存在负环
    for(int i=0;i<n;i++){
        if(mp[i][i]<0){
            flag=true;
        }
    } 
    if(flag) cout<<"存在负环"<<endl;
    else cout<<"不存在负环"<<endl; 
//  cout<<mp[0][n-1]<<endl; 
    return 0;
}

1. 3.优先队列优化(记录路径)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>

using namespace std;
#define pa pair<int,int>
int n,m;
const long long inf=0x3f3f3f3f3f3f3f3f;

int fa[10000];//记录路径
long long d[10000];
//int flag[10000];
struct pr{
	int to,cost;
};
struct pe{
	int x,y;
	bool friend operator< (pe s,pe t)
	{
		if(s.x !=t.x) return s.x>t.x;
		else return s.y>t.y;
	}
};
vector<pr>ve[10000];
void bfs()
{
	priority_queue<pe>q;
	fill(d+1,d+n+1,inf);
//	memset(flag,0,sizeof flag);
	d[1]=0;
	//flag[1]=1;
	pe c;
	c.x =0,c.y=1;
	q.push(c);
	while(!q.empty() ) 
	{
		c=q.top() ;
		q.pop() ;
		int v=c.y;
		if(v==n)
		{
			break;
		}
		if(d[v]<c.x ) continue;
		for(int i=0;i<ve[v].size();i++)
		{
			pr e=ve[v][i];
			if((d[e.to]>d[v]+e.cost))
			{	
				d[e.to]=d[v]+e.cost ;
				fa[e.to]=v ;
			//	flag[e.to]=1;
				c.x =d[e.to];c.y =e.to;
				q.push(c); 
			}
		}
	}
}

int main()
{
	int x,y,s;
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++)
	{
		scanf("%d%d%d",&x,&y,&s);
		pr c;
		c.cost =s;c.to =y;
		ve[x].push_back(c);
	}
	bfs();
	printf("%lld\n",d[n]);
	printf("%d",n);
	while(fa[n]!=1)
	{
		printf(" %d",fa[n]);
		n=fa[n];
	}
	printf(" 1\n");
	return 0;
}

2.拓扑排序

#include<vector>
#include<queue>
#include<iostream>
#include<cstring>
using namespace std;
int in[10100];// 存入度 
vector<int>v[10100];// 存关系 构建图 
int main()
{
    int m,n;
    int x,y;
    while(cin>>n>>m)// 根据题目要求可以改动 
    {
        memset(in,0,sizeof(in));// 清空入度 
        for(int i=1;i<=n;i++) v[i].clear() ;// 清空vector 
        while(m--)// m组数据 
        {
            cin>>y>>x;
            in[y]++;// y的关系大于x,x指向y y的入度+1; 
            v[x].push_back(y);// 就 y 放在 x后面 
        }
        queue<int>q;// 定义一个队列 最为节点的删除 
        for(int i=1;i<=n;i++)
        {
            if(!in[i]) {        // 入度为零的节点放入 队列   
                q.push(i);
            }    
        }
        while(!q.empty() )
        {
            int xx=q.front() ; //  如果队列中一次存了大于 2 个节点 
            q.pop() ;         //说明该图有 2->3 && 2->4 这种情况 有点个点之间没有关系
            n--;             // 总节点数 -1; 
            for(int i=0;i<v[xx].size() ;i++)  // 遍历这个节点后面的 点 
            {
                int yy=v[xx][i];
                in[yy]--;                    //  删除 x 后 yy 的入度就 -1;  
                if(!in[yy]) {       //  如果此时 yy 入度为零放入队列 遍历他的下一个节点 
                   q.push(yy);   
                } 
            }
        }
        if(n) cout<<"该图有环"<<endl;  // 如果总结点数没减为零 说明有环的存在    
    }
    return 0;
}

3.LCA

3.1倍增LCA

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>

using namespace std;
#define pa pair<int,int>
const int mn=1e5;
int fa[mn][60];//记录2的j次方个节点是谁 
int depth[mn];//记录深度 
int bit[mn];//作为2进制 
int in[mn];//存入度方便找根节点 
int dis[mn];
vector<pa>ve[mn];//存图; 

void dfs(int x,int y,int s)
{
	depth[x]=depth[y]+1;//x是y的儿子 所以深度加一 
	dis[x]=dis[y]+s;
	fa[x][0]=y;
	for(int i=1;i<=29;i++) fa[x][i]=fa[fa[x][i-1]][i-1];//这个节点的的第2的j次方等于2的j-1次方的那个值得2的j-1次方; 
	for(int i=0;i<ve[x].size() ;i++)                  // 建议画个图理解一下 
	{
		//pa xx=ve[x][i];
		int X=ve[x][i].first;
		int Y=ve[x][i].second;
		if(x==X) continue;//这里用的双向存图所以一旦xx=其父节点 
		//depth[xx]=depth[y]+1;就可以不管了 
		dfs(X,x,Y);//继续搜索 
	}
}
int lca(int x,int y)
{
	if(depth[y]>depth[x])//让x比y深 
	{
		swap(x,y);
	}
	int dif=depth[x]-depth[y];//取差值方便以后进行调深度 
	for(int i=29;i>=0;i--)
	{
		if(dif>=bit[i])//调到同一深度 
		{
			x=fa[x][i];
			dif-=bit[i];
		}
	}
	//这个也是调到同一深度 
	if(x==y) return x;//如果现在节点已经相同那么就输出就行了 
	for(int i=29;i>=0;i--)
	{//从大到小慢慢遍历合适的节点; 
		if(depth[x]>=bit[i]&&fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}
int main()
{
	int n,m,t,x,y,d;
	bit[0]=1;
	for(int i=1;i<=29;i++)
	{
		bit[i]=bit[i-1]<<1;
	}
	while(~scanf("%d",&t))
	{
		while(t--)
		{
			scanf("%d%d",&n,&m);
			for(int i=1;i<=n;i++)
			{
				depth[i]=0;
				dis[i]=0;
				in[i]=0;
				ve[i].clear() ;
			}
			memset(fa,0,sizeof fa);
			//	printf("%dssssssss\n",bit[8]);
			for(int i=1;i<n;i++)
			{
				scanf("%d%d%d",&x,&y,&d);
				ve[x].push_back(make_pair(y,d));//因为不确定谁是定点就双向存图; 
				in[y]++;
			}
			int s;
			for(int i=1;i<=n;i++)
			{
				if(in[i]==0) {
					s=i;
					break;
				}
			}
			dfs(s,0,0);
			while(m--)
			{
				scanf("%d%d",&x,&y);
				int ss=lca(x,y);
				printf("%d\n",dis[x]+dis[y]-2*dis[ss]);
			}
		}
	}
	return 0;
}

3.2 tarjan

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>

using namespace std;
#define in =read()
#define ll long long 
const int size = 500000 + 50;
ll n,m,s;
ll site1,site2;
ll head[size],father[size],vis[size],qhead[size],num[size];

struct apoint{
    ll x; ll y;
}a[2*size];
struct qpoint{
    ll x; ll y; ll z;
}q[2*size];

inline ll read()
{
    ll num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

ll find(ll x)
{
    if(father[x]!=x)father[x]=find(father[x]);
    return father[x];
}

inline void unionn(ll x,ll y)
{
    ll xx=find(x),yy=find(y);
    father[yy]=xx;
}

inline void add(ll x,ll y)
{
    a[++site1].x=head[x];
    a[site1].y=y;
    head[x]=site1;
}

inline void qadd(ll x,ll y,ll z)
{
    q[++site2].x=qhead[x];
    q[site2].y=y;
    q[site2].z=z;
    qhead[x]=site2;
}

inline void tarjan(ll x)
{
    vis[x]=1;
    int y,z;
    for(int i=head[x];i;i=a[i].x){
        y=a[i].y;
        if(!vis[y]){
            tarjan(y); unionn(x,y);
        }
    }
    for(int i=qhead[x];i;i=q[i].x){
        y=q[i].y; z=q[i].z;
        if(vis[y])num[z]=find(y);
    }
}

int main()
{
    n in; m in; s in;
    int x,y;
    for(int i=1;i<n;i++){
        x in; y in;
        add(x,y); add(y,x);
    }
    for(int i=1;i<=n;i++)father[i]=i;
    for(int i=1;i<=m;i++){
        x in; y in;
        qadd(x,y,i); qadd(y,x,i);
    }
    tarjan(s);
    for(int i=1;i<=m;i++){
        printf("%d\n",num[i]);
    }
}

4.树的直径

//**************************dfs*********************
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,tot,a,b,ans,pos;
int first[200010],nxt[200010];
struct edge
{
    int u,v;
}l[200010];
void build(int f,int t)
{
    l[++tot]=(edge){f,t};
    nxt[tot]=first[f];
    first[f]=tot;
}
void dfs(int k,int fa,int d)
{
    if(d>ans)
    {
        ans=d;
        pos=k;
    }
    for(int i=first[k];i!=-1;i=nxt[i])
    {
        int x=l[i].v;
        if(x==fa)
        continue;
        dfs(x,k,d+1);
    }
}
int main()
{
    memset(first,-1,sizeof(first));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a,&b);
        if(a!=0)
        {
            build(i,a);
            build(a,i);
        }
        if(b!=0)
        {
            build(i,b);
            build(b,i);
        }
    }
    dfs(1,1,0);//第一遍DFS找出树上深度最深的点
    ans=0;
    dfs(pos,1,0);//第二遍DFS由深度最深的点向上找最长链
    printf("%d",ans);
    return 0;   
}
/////////////////////////////////////bfs************************************
//记录各个节点到其最远的距离
#include<iostream>
#include<algorithm> 
#include<iostream>
#include<cstring>
#include<vector>

using namespace std;

typedef pair<int,int> pa;
int n,maxlen,s;
vector<pa>v[100100];
int dp[100100];

int dfs(int x,int y,int l)
{
	if(maxlen<=l)
	{
		maxlen=l;
		s=x;
	}
	pa p;
	for(int i=0;i<v[x].size() ;i++)
	{
		p=v[x][i];
		if(p.first ==y) continue;
		else
		{	
			dfs(p.first ,x,l+p.second);
			dp[p.first]=max(dp[p.first],l+p.second);
		}
	}
}

int main()
{
	while(cin>>n)
	{
		int x,y;
		memset(dp,0,sizeof dp);
		for(int i=0;i<=n;i++) v[i].clear() ;
		for(int  i=2;i<=n;i++)
		{
			cin>>x>>y;
			v[x].push_back(make_pair(i,y));
			v[i].push_back(make_pair(x,y));  
		}
		s=0;
		maxlen=0;
		maxlen=0;
		dfs(1,-1,0);
		dfs(s,-1,0);
		dfs(s,-1,0);
		for(int i=1;i<=n;i++) cout<<dp[i]<<endl;
	}
	return 0;
} 

并查集(带权)

#include<string.h>
#include<stdio.h>
#include<algorithm>

using namespace std;

int fa[30500];// 存节点 
int a[30500];// 以该节点为父节点的节点一共有几个 
int b[30200];//  该节点到其父节点的距离 

int find(int x)
{// 整个程序的核心算法 递归用的真是666666 
	if(fa[x]!=x)
	{
		int s=fa[x];// 将其上一个节点的值付给s 
		fa[x]=find(fa[x]);
		a[x]+=a[s];//x到其祖先节点的值等于他到他父节点的值加
				  //上起父节点到其祖先节点的距离 
	}
	return fa[x];
}

void jion(int x,int y)
{
	int xx=find(x);
	int yy=find(y);
	if(xx!=yy)
	{
		fa[yy]=xx;
		a[yy]+=b[xx];//因为把yy的父节点接到xx的父节点后面了 
		b[xx]+=b[yy];//所以yy到最终祖先节点的距离等于他到本来的祖先的距离 
	}  //加上xx到其祖先节点的距离,此时新的祖先节点的子孙 
}	//数量等于 以前 xx 的子孙加上 yy 的祖孙; 

int main()
{
	int n,x,y;
	char c;
	while(~scanf("%d",&n))
	{
		for(int i=1;i<=30009;i++)
		{
			a[i]=0;//自己到自己的距离为0;
			b[i]=1;//刚开始的时候每个节点都是一个祖先节点包含自己所以为1;
			fa[i]=i;//第i个值为自己方便以后找祖先节点
		}
		while(n--)
		{
			scanf(" %c",&c);
			if(c=='M')
			{
				scanf("%d%d",&x,&y);
				jion(x,y);
			}
			else {
				scanf("%d",&x);
				int s=find(x);//查找 x的祖先节点 
				printf("%d\n",b[s]-a[x]-1);
			}//  x 下面的节点等于总结点数减去x到祖先节点的个数 
		}
	}
	return 0;
}

二,数论

2.1矩阵快速幂

#include<iostream>
#include<algorithm>
#include<string.h>

using namespace std;

long long t;
long long mod=2147493647;
long long n;
long long bb[3];
struct pe{
	long long a[8][8];
};
pe mul(pe x,pe y)
{
	pe z;memset(z.a,0,sizeof z.a);
	for(int i=0;i<7;i++)
	{
		for(int j=0;j<7;j++)
		{
			for(int k=0;k<7;k++)
			z.a[i][j]=(x.a[i][k]*y.a[k][j]%mod+z.a[i][j])%mod;
		}
	}
	return z;
}

pe poow(pe x,int b)
{
	pe ans;memset(ans.a,0,sizeof ans.a);
	for(int i=0;i<7;i++) ans.a[i][i]=1;
	while(b)
	{
		if(b&1) ans=mul(ans,x);
		x=mul(x,x);
		b>>=1;
	}
	return ans;
}

int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n>>bb[1]>>bb[2];
		if(n<=2)
		{
			cout<<bb[n]%mod<<endl;
			continue;
		}
		pe a,b;//需要自己根据题意构造矩阵
		pe x=mul(poow(b,n-2),a);
		cout<<x.a[0][0]<<endl;
	}
	return 0;
}

2.2博弈论

Bash–两人从一堆a个石子里面轮流取石子,每次最多去b个,取到最后一个石子获胜

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		int a, b,flag;
		scanf("%d%d", &a, &b);
		if (a % (b + 1) == 0)flag = 2;
		else flag = 1;
		if (flag == 1)printf("A\n");
		else printf("B\n");
	}
}

Nim–两人从n堆石子中任选一堆取石子,不得不取,可以把一堆直接去玩,拿到最后一颗石子得人获胜

int main() {
	int n;
	int stone, tag=0;
	scanf("%d", &n);
	while (n--) {
		
		scanf("%d", &stone);
		tag ^= stone;
	}
	// tag为0则为后⼿手赢,否则为先⼿手赢 
	if (tag == 0)printf("B\n");
	else printf("A\n");
}

威佐夫博弈–有2堆石子。A B两个人轮流拿,A先拿。每次可以从一堆中取任意个或从2堆中取相同数量的石子,但不可不取。拿到最后1颗石子的人获胜

int main() {
	int n;
	int stone, tag=0;
	scanf("%d", &n);
	while (n--) {
		int a, b;
		scanf("%d%d", &a,&b);
		if (a < b)swap(a, b);
		int flag = (a - b)*(sqrt(5) + 1) / 2;  //黄金分割线
		//  如果flag == b,则为后⼿手赢,否则先⼿手赢(奇异局)
		if (flag == b)printf("B\n");
		else printf("A\n");
	}
}

SG打表

//f[]:可以取走的石子个数
//sg[]:0~n的SG函数值
//hash[]:mex{}
int f[N],sg[N],hash[N];     
void getSG(int n)
{
    int i,j;
    memset(sg,0,sizeof(sg));
    for(i=1;i<=n;i++)
    {
        memset(hash,0,sizeof(hash));
        for(j=1;f[j]<=i;j++)
            hash[sg[i-f[j]]]=1;
        for(j=0;j<=n;j++)    //求mes{}中未出现的最小的非负整数
        {
            if(hash[j]==0)
            {
                sg[i]=j;
                break;
            }
        }
    }
}

SG-DFS

int s[110],sg[10010],n;
int SG_dfs(int x)
{
    int i;
    if(sg[x]!=-1)
        return sg[x];
    bool vis[110];
    memset(vis,0,sizeof(vis));
    for(i=0;i<n;i++)
    {
        if(x>=s[i])
        {
            SG_dfs(x-s[i]);
            vis[sg[x-s[i]]]=1;
        }
    }
    int e;
    for(i=0;;i++)
        if(!vis[i])
        {
            e=i;
            break;
        }
    return sg[x]=e;
}
一般DFS只在打表解决不了的情况下用,首选打表预处理。

2.3中国剩余定理

两两互质

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
 
long long aa[15],bb[15];
 
long long extgcd(long long a, long long b, long long &x, long long &y){
    long long d = a;
    if(b != 0){
        d = extgcd(b, a%b, y, x);
        y -= (a / b) * x;
    }
    else{
        x = 1; y = 0;
    }
    return d;
}
// 注意使用方法, 除以mi 余数是ai, 传参数的时候容易传错, 倒过来再试试
// 求解模线性方程组x = ai (mod mi)
long long China(long long a[], long long m[], int k)
{
    long long M = 1;
    long long ans = 0;
    for(int i = 0; i < k; i++)
        M *= m[i];
    for(int i=0; i<k; i++){
        long long x, y;
        long long Mi = M / m[i];
        extgcd(Mi, m[i], x, y);
        ans = (ans + Mi * x * a[i]) % M;
    }
    if(ans < 0)
        ans += M;
    return ans;
}
 
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i = 0; i < n; i++)
            scanf("%I64d %I64d",&aa[i],&bb[i]);
        printf("%I64d\n",China(bb, aa, n));
    }
    return 0;
}

不两两互质

long long extgcd(long long a, long long b, long long &x, long long &y){
    long long d = a;
    if(b != 0){
        d = extgcd(b, a%b, y, x);
        y -= (a / b) * x;
    }
    else{
        x = 1; y = 0;
    }
    return d;
}
 
//求模线性方程组x = bi (mod ni), ni可以不互质, num表示方程的数目
long long China(long long b[], long long n[], int num){
    bool flag = false;
    long long bb, d, t, k;
    long long n1 = n[0], n2;
    long long b1 = b[0], b2;
    long long x, y;
    for(int i = 1; i < num; i++){
        n2 = n[i], b2 = b[i];
        bb = b2 - b1;
        d = extgcd(n1, n2, x, y);
        if (bb % d){            //模线性解k1时发现无解
            flag = true;
            break;
        }
        k = bb / d * x;         //相当于求上面所说的k1【模线性方程】
        t = n2 / d;
        if (t < 0) t = -t;
        k = (k % t + t) % t;    //相当于求上面的K`
        b1 = b1 + n1*k;
        n1 = n1 / d * n2;
    }
    if (flag)
        return -1;               //无解返回-1
 
    return ((b1%n1)+n1)%n1;     //这是返回的最小非负整数解
                                //形成的所有解:b1, b1+n1, b1+2n1,..., b1+xni...
/*
    下面是求不大于N的解的个数
    if (b1 > N)
        return 0;
    return (N-b1)/n1+1;
*/
}

2.4.快速乘&&快速幂

typedef long long ll;
ll q_mul(ll a, ll b, ll mod){
	ll ans=0;
	while(b){
		if(b & 1)	ans=(ans+a)%mod;
		a=(a<<1)%mod;
		b>>=1;
	}
	return ans;
}
///////////////////////////第二种/////////////////////////////////
inline ll ksc(ll x,ll y,ll mod)
{
    return (x*y-(ll)((long double)x/mod*y)*mod+mod)%mod;     
}

2.5.Miller-Rabin素数测试算法

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int prime[10]={2,3,5,7,11,13,17,19,23,29};
int Quick_Multiply(int a,int b,int c)  //快速积(和快速幂差不多) 
{
    long long ans=0,res=a;
    while(b)
    {
        if(b&1)
          ans=(ans+res)%c;
        res=(res+res)%c;
        b>>=1;
    }
    return (int)ans;
}
int Quick_Power(int a,int b,int c)     //快速幂,这里就不赘述了 
{
    int ans=1,res=a;
    while(b)
    {
        if(b&1)
          ans=Quick_Multiply(ans,res,c);
        res=Quick_Multiply(res,res,c);
        b>>=1;
    }
    return ans;
}
bool Miller_Rabin(int x)     //判断素数 
{
    int i,j,k;
    int s=0,t=x-1;
    if(x==2)  return true;   //2是素数 
    if(x<2||!(x&1))  return false;     //如果x是偶数或者是0,1,那它不是素数 
    while(!(t&1))  //将x分解成(2^s)*t的样子 
    {
        s++;
        t>>=1;
    }
    for(i=0;i<10&&prime[i]<x;++i)      //随便选一个素数进行测试 
    {
        int a=prime[i];
        int b=Quick_Power(a,t,x);      //先算出a^t
        for(j=1;j<=s;++j)    //然后进行s次平方 
        {
            k=Quick_Multiply(b,b,x);   //求b的平方 
            if(k==1&&b!=1&&b!=x-1)     //用二次探测判断 
              return false;
            b=k;
        }
        if(b!=1)  return false;   //用费马小定律判断 
    }
    return true;   //如果进行多次测试都是对的,那么x就很有可能是素数 
}
int main()
{
    int x;
    scanf("%d",&x);
    if(Miller_Rabin(x))  printf("Yes");
    else  printf("No");
    return 0;
}

2.6. 扩展欧几里德

非递归

int exgcd(int m, int n, int &x, int &y) {
    if (n == 0) {
        x = 1; y = 0;
        return m;
    }
    int a, a1, b, b1, c, d, q, r, t;
    a1 = b = 1;
    a = b1 = 0;
    c = m; d = n;

    q = c/d; r = c%d;
    while (r) {
        c = d;
        d = r;
        t = a1;
        a1 = a;
        a = t - q * a;
        t = b1;
        b1 = b;
        b = t - q * b;

        q = c/d;
        r = c%d;
    }
    x = a; y = b;
    return d;
}

递归

int exgcd(int a, int b, int &x, int &y)
{
    if(b == 0)
    {//推理1,终止条件
        x = 1;
        y = 0;
        return a;
    }
    int r = exgcd(b, a%b, x, y);
    //先得到更底层的x2,y2,再根据计算好的x2,y2计算x1,y1。
    //推理2,递推关系
    int t = y;
    y = x - (a/b) * y;
    x = t;
    return r;
}

三,树

树状数组

// hdu1166-区间求和
#include<bits/stdc++.h>
#define maxn 50005
#define lowbit(x) (x & (-x))
using namespace std;
int tree[50005];
int n, t;
void update(int x, int v) {
	while(x <= n) {
		tree[x] += v;
		x += lowbit(x);
	}
}
int getsum(int x) {
	int ans = 0;
	while(x) {
		ans += tree[x];
		x -= lowbit(x);
	}
	return ans;
}
int getq(int l ,int r) {
	int ans = getsum(r) - getsum(l - 1);
	return ans;
}
int main() {
	int a = 0;
	char str[30];
	scanf("%d",&t);
	while(t--) {
		memset(tree, 0, sizeof(tree));
		a++;
		int k;
		scanf("%d",&n);
		for(int i =1; i <= n; i++) {
			scanf("%d",&k);
			update(i, k);
		}
		printf("Case %d:\n", a);
		while(~scanf("%s",str)) {
			if(str[0] == 'E') {
				break;
			}
			int i, j;
			if(str[0] == 'A') {
				scanf("%d%d",&i, &j);
				update(i, j);
			} else if(str[0] == 'Q') {
				scanf("%d%d",&i, &j);
				printf("%d\n",getq(i, j));
			} else {
				scanf("%d%d",&i, &j);
				update(i, -j);
			}
		}
	}
	return 0;
}

线段树

// 区间最大值
#include<cstdio>
#include<algorithm>
#define maxn 100105
using namespace std;
int ls[maxn*20],rs[maxn*20];  
int tot,sum[maxn*20],root[maxn];//tot是总的节点数,sum是记录每个数在某个节点出现的次数,root是每个线段树的根的值;
void build(int l,int r,int &st)  //st表示地址
{
    st=++tot;
    sum[st]=0;
    if(l==r)
        return;
    int temp=(l+r)/2;
    build(l,temp,ls[st]); 
    build(temp+1,r,rs[st]);
}
void update(int last,int k,int l,int r,int &st)
{
    st=++tot;
    ls[st]=ls[last];
    rs[st]=rs[last];
    sum[st]=sum[last]+1;
    if(l==r)
        return;
    int temp=(l+r)/2;
    if(k<=temp)
        update(ls[last],k,l,temp,ls[st]);
    else
        update(rs[last],k,temp+1,r,rs[st]);
}
int query(int ss,int tt,int k,int l,int r)
{
    if(l==r)
        return l;
    int ans=sum[ls[tt]]-sum[ls[ss]];
    int temp=(l+r)/2;
    if(k<=ans)
        return query(ls[ss],ls[tt],k,l,temp);
    else
        return query(rs[ss],rs[tt],k-ans,temp+1,r);
}
int a[maxn],b[maxn];
int main()
{
    int T;
    int n,m;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        tot=0;
        sort(b+1,b+n+1);
        int t=unique(b+1,b+n+1)-b-1;
        build(1,t,root[0]);
        for(int i=1;i<=n;i++)
            a[i]=lower_bound(b+1,b+t+1,a[i])-b;
        for(int i=1;i<=n;i++)
        {
            update(root[i-1],a[i],1,t,root[i]);
        }
        for(int i=1;i<=m;i++)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            int ans=query(root[l-1],root[r],k,1,t);
            printf("%d\n",b[ans]);
        }
    }
    return 0;
}

四,网络流

最大流

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int inf=1e9;

int n,m,x,y,z,maxflow,deep[500];//deep深度 
struct Edge{
    int next,to,dis;
}edge[500];
int num_edge=-1,head[500],cur[500];//cur用于复制head 
queue <int> q;

void add_edge(int from,int to,int dis,bool flag)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    if (flag) edge[num_edge].dis=dis;//反图的边权为 0
    head[from]=num_edge;
}

//bfs用来分层 
bool bfs(int s,int t)
{
    memset(deep,0x7f,sizeof(deep));
    while (!q.empty()) q.pop();
    for (int i=1; i<=n; i++) cur[i]=head[i];
    deep[s]=0;
    q.push(s);

    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i!=-1; i=edge[i].next)
        {
            if (deep[edge[i].to]>inf && edge[i].dis)//dis在此处用来做标记 是正图还是返图 
            {
                deep[edge[i].to]=deep[now]+1;
                q.push(edge[i].to);
            }
        }
    }
    if (deep[t]<inf) return true;
    else return false;
}

//dfs找增加的流的量 
int dfs(int now,int t,int limit)//limit为源点到这个点的路径上的最小边权 
{
    if (!limit || now==t) return limit;

    int flow=0,f;
    for (int i=cur[now]; i!=-1; i=edge[i].next)
    {
        cur[now]=i;
        if (deep[edge[i].to]==deep[now]+1 && (f=dfs(edge[i].to,t,min(limit,edge[i].dis))))
        {
            flow+=f;
            limit-=f;
            edge[i].dis-=f;
            edge[i^1].dis+=f;
            if (!limit) break;
        }
    }
    return flow;
}

void Dinic(int s,int t)
{
    while (bfs(s,t))
        maxflow+=dfs(s,t,inf);
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&m,&n);
    for (int i=1; i<=m; i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add_edge(x,y,z,1); add_edge(y,x,z,0);
    }
    Dinic(1,n);
    printf("%d",maxflow);
    return 0;
}

最小费用流

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=100010;

bool vis[maxn];
int n,m,s,t,x,y,z,f,dis[maxn],pre[maxn],last[maxn],flow[maxn],maxflow,mincost;
//dis最小花费;pre每个点的前驱;last每个点的所连的前一条边;flow源点到此处的流量 
struct Edge{
    int to,next,flow,dis;//flow流量 dis花费 
}edge[maxn];
int head[maxn],num_edge; 
queue <int> q;

void add_edge(int from,int to,int flow,int dis)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    edge[num_edge].flow=flow;
    edge[num_edge].dis=dis;
    head[from]=num_edge;
}

bool spfa(int s,int t)
{
    memset(dis,0x7f,sizeof(dis));
    memset(flow,0x7f,sizeof(flow));
    memset(vis,0,sizeof(vis));
    q.push(s); vis[s]=1; dis[s]=0; pre[t]=-1;

    while (!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for (int i=head[now]; i!=-1; i=edge[i].next)
        {
            if (edge[i].flow>0 && dis[edge[i].to]>dis[now]+edge[i].dis)//正边 
            {
                dis[edge[i].to]=dis[now]+edge[i].dis;
                pre[edge[i].to]=now;
                last[edge[i].to]=i;
                flow[edge[i].to]=min(flow[now],edge[i].flow);//
                if (!vis[edge[i].to])
                {
                    vis[edge[i].to]=1;
                    q.push(edge[i].to);
                }
            }
        }
    }
    return pre[t]!=-1;
}

void MCMF()
{
    while (spfa(s,t))
    {
        int now=t;
        maxflow+=flow[t];
        mincost+=flow[t]*dis[t];
        while (now!=s)
        {//从源点一直回溯到汇点 
            edge[last[now]].flow-=flow[t];//flow和dis容易搞混 
            edge[last[now]^1].flow+=flow[t];
            now=pre[now];
        }
    }
}

int main()
{
    memset(head,-1,sizeof(head)); num_edge=-1;//初始化 
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for (int i=1; i<=m; i++)
    {
        scanf("%d%d%d%d",&x,&y,&z,&f);
        add_edge(x,y,z,f); add_edge(y,x,0,-f);
        //反边的流量为0,花费是相反数 
    }
    MCMF();
    printf("%d %d",maxflow,mincost);
    return 0;
}

五,字符串

KMP

#include<bits/stdc++.h>
using namespace std;
const int Maxn=1000005;
char s1[Maxn],s2[Maxn];
int l1,l2,fail[Maxn];
void Fail(){//构造fail数组 
	for(int i=2,j=0;i<=l2;++i){
		while(j&&s2[j+1]!=s2[i])j=fail[j];
		if(s2[j+1]==s2[i])++j;
		fail[i]=j;
	}
}
void Match(){
	for(int i=1,j=0;i<=l1;++i){
		while(j&&s2[j+1]!=s1[i])j=fail[j];
		if(s2[j+1]==s1[i])++j;
		if(j==l2){
			//找到一个串,自行维护 
			j=fail[j];
		}
	}
}
int main(){
	scanf("%s%s",s1+1,s2+1);
	l1=strlen(s1+1);
	l2=strlen(s2+1);
	Fail();
	Match();
	return 0;
}

哈希

#include <cstdio>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstring>
#define LL long long
using namespace std;
const LL base1=131;
const LL base2=131;
const LL mod1=1e9+7;
const LL mod2=1e9+9;
char ss[10][10010];
struct node{
    LL hash1,hash2;
}a[10011];
LL make_hash1(char s[])
{
    LL ans=0;
    for(int i=0;i<strlen(s);i++)
     ans=(ans*base1+(LL)(s[i]))%mod1;
    return ans;
}
LL make_hash2(char s[])
{
    LL ans=0;
    for(int i=0;i<strlen(s);i++)
     ans=(ans*base2+(LL)(s[i]))%mod2;
    return ans;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
     scanf("%s",ss[i]),a[i].hash1=make_hash1(ss[i]),a[i].hash2=make_hash2(ss[i]);
 
    cout<<"***********"<<endl;
    for(int i=1;i<=n;i++)
    {
        cout<<ss[i]<<"  "<<a[i].hash1<<"   "<<a[i].hash2<<endl;
    }
    return 0;
}//求子串哈希值hash=((hash[r]−hash[l−1]∗mod^(r−l+1))%mod+mod)%mod

六,外挂

整数

inline int read(){
 2     int x=0,f=1;
 3     char ch=getchar();
 4     while(ch<'0'||ch>'9'){
 5         if(ch=='-')
 6             f=-1;
 7         ch=getchar();
 8     }
 9     while(ch>='0'&&ch<='9'){
10         x=(x<<1)+(x<<3)+(ch^48);
11         ch=getchar();
12     }
13     return x*f;
14 }

浮点数

inline bool scan_lf( double &num )
{
    char in;
    double Dec = 0.1;
    bool IsN = false;
    bool IsD = false;
    in = getchar();
    if ( in==EOF )
        return false;
    while ( in!='-'&&in!='.'&&(in<'0'||in>'9') )
        in = getchar();
    if ( in=='-' )
        IsN = true,num = 0;
    else if ( in=='.' )
        IsD = true,num = 0;
    else
        num = in-'0';
    if ( !IsD )
    {
        while ( in=getchar(),in>='0'&&in<='9' )
            num = num*10+in-'0';
    }
    if ( in!='.' )
    {
        if ( IsN )
            num = -num;
        return true;
    }
    else
    {
        while ( in=getchar(),in>='0'&&in<='9' )
            num += Dec*(in-'0'),Dec *= 0.1;
    }
    if ( IsN )
        num = -num;
    return true;
}
发布了49 篇原创文章 · 获赞 14 · 访问量 4363

猜你喜欢

转载自blog.csdn.net/qq_43750980/article/details/99718340