算法模板代码整合

【并查集】

int father[5010];  
int get(int a)  
{  
    if(father[a]==a)  
        return a;  
    else  
        return father[a]=get(father[a]);  
}  
  
void add(int a,int b)  
{  
    a=get(a);  
    b=get(b);  
    if(a!=b)  
    {  
        father[a]=b;  //注意是让b的根节点成为"a的根节点"的根节点  
    }  
}  
  
  
int main()  
{  
    int n,m,p;  
    cin>>n;  
    for(int i=1;i<=n;i++)  
        father[i]=i;     //注意初始化
    cin>>m;  
    cin>>p;  
    while(m--)  
    {  
        int a,b;  
        cin>>a>>b;  
        add(a,b);  
    }  
    while(p--)  
    {  
        int a,b;  
        cin>>a>>b;  
        if(get(a)==get(b))  
            cout<<"Yes";  
        else  
            cout<<"No";  
        if(p!=0)  
            cout<<endl;  
    }  
    return 0;  
}  


【带权并查集】

#include<iostream>  
#include<bits/stdc++.h>  
using namespace std;  
  
int father[30010];  
int dist[30010];  
int size[30010];  
  
int get(int a)  
{  
    if(father[a]==a)  
        return a;  
    int y=father[a];  
    father[a]=get(y);  
    dist[a]+=dist[y];  
    return father[a];  
}  
  
void merge(int aa,int bb)  
{  
    int a=get(aa);  
    int b=get(bb);  
    if(a!=b)  
    {  
        father[a]=b;  
        dist[a]=size[b];  
        size[b]+=size[a];  
    }  
}  
  
int main()  
{  
    memset(dist,0,sizeof(dist));  
    for(int i=0;i<30010;i++)  
    {  
       father[i]=i;    
        size[i]=1;    //2个初始化。
    }    


【bfs】

#include<queue>  
int xx[4]={0,0,1,-1};  
int yy[4]={1,-1,0,0};  
int step[150][150];  
struct point  
{  
    int x,y;  
    point(int xx,int yy)  
    {  
        x=xx;  
        y=yy;  
    }  
};  
queue<point> q;  
int visit[150][150];  
static int n,m;  
char b[150][150];  
int flag=0;  
void bfs(int sx,int sy)  
{  
    q.push(point(sx,sy));  
    visit[sx][sy]=1;  
    while(q.empty()!=true)  
    {  
        point a=q.front();  
        q.pop();  
        for(int i=0;i<4;i++)  
        {  
            int xxx=a.x+xx[i];  
            int yyy=a.y+yy[i];  
            if(xxx<0 || yyy<0 || xxx>n-1 || yyy>m-1 || b[xxx][yyy]=='#' ||visit[xxx][yyy]==1)  
                continue;  
            visit[xxx][yyy]=1;  
            q.push(point(xxx,yyy));  
            step[xxx][yyy]=step[a.x][a.y]+1;  //最重要,bfs第一次走到的即最短路  
            if(b[xxx][yyy]=='T')  
                flag=1;  
        }  
    }  
}  

注意:入栈即访问。


【多重背包】

for(int i=1;i<=种类数;i++)  
    {  
        int k=1;     //对于每一种,k准备取1 2 4 8...  
        int temp=m[i];  //m[i]为第i种的数量  
        for(k; k<=temp ; k*=2)  
        {  
            value[++num]=k*v[i];  //v[i]为第i种的每个的价值  
            temp-=k;  
        }  
        if(temp>0)  
            value[++num]=temp*v[i];    
    }

这样就可以得到所有种类任意价值数的组合。

多重背包其实就是这样的,先把value数组给弄好了,然后再把value数组的元素每一个都看成一个单独的背包,对这些背包来做01背包的操作即可。

for(int i=0;i<=num;i++)  
    {  
        for(long long j=m;j>=value[i];j--)  
        {  
            dp[j]=max(dp[j],dp[j-value[i]]+value[i]);  
        }  
    }


【LIS最长上升子序列】



【LCS最长公共子序列】


注意算法细节:i和j都是从下标1开始的。所以两个字符数组的输入应该也从下标1开始存。


【状态压缩dp】

https://blog.csdn.net/m0_38033475/article/details/79497154

【枚举子集】

for(int i=t;i;i=(i-1)&t)
{
	dp[t]=min(dp[t],dp[i]+dp[i^t]); //这里dp[t]表示选取情况为t二进制时xx操作取得的最小操作数 
}

状态压缩dp:

①n很小,不超过20

②组合问题(选取问题——二进制枚举来表示选取情况)

③求最值(直接联想到动态规划)



【gcd求最大公约数】

int gcd(int a,int b)
{
	if(b==0)
		return a;
	return gcd(b,a%b);
}

最小公倍数= a*b/gcd(a,b)


【素数打表】

int f[maxn]; //全局变量,默认为0.素数为0,合数为1 

f[1]=1;
for(int i=2;i*i<=n;i++)
{
	if(f[i]==0)
	{
		for(int j=i*i;j<=n;j+=i)
		{
			f[j]=1;
		}
	}
} 


【欧拉公式求互质数个数】(求有多少个i (i属于1~n) 使得 gcd(i ,n)==1 )

求很多次,打表法:https://blog.csdn.net/m0_38033475/article/details/79510301

只求几次,直接用公式 phi(n)=n*(1-1/p1)*(1-1/p2)...*(1-1/pi)   p是它的质因子(不重复)


【拓展欧几里得】

扩展欧几里德算法是用来在已知a, b求解一组x,y,使它们满足贝祖等式: ax+by = gcd(a, b) =d

long long exgcd(long long a,long long b,long long &x,long long &y)  
{  
    if(b==0)  
    {  
        x=1;  
        y=0;  
        return a;  
    }  
    long long r=gcd1(b,a%b,x,y);  
    long long t=x;  
    x=y;  
    y=t-a/b*y;  
    return r;  
}  

【二分快速幂】

int pow_mod(int a,int b,int mod)
{
	if(b==0)
		return 1%mod;
	int temp=pow_mod(a,b/2,mod);
	temp=temp*temp%mod;
	if(b%2==1)
		temp=temp*a%mod;
	return temp;
}


【矩阵二分快速幂 优化dp】

int n; // 所有矩阵都是 n * n 的矩阵  
struct matrix {  
   int a[100][100];    //这个维度倒无所谓,根据实际维度定也可以,不会这么大的 - - 因为是你手写推出来的矩阵。  
};  
matrix matrix_mul(matrix A, matrix B, int mod) {  
    // 2 个矩阵相乘  
    matrix C;  
    for (int i = 0; i < n; ++i) {  
        for (int j = 0; j < n; ++j) {  
            C.a[i][j] = 0;  
            for (int k = 0; k < n; ++k) {  
                C.a[i][j] += A.a[i][k] * B.a[k][j] % mod;  
                C.a[i][j] %= mod;  
            }  
        }  
    }  
    return C;  
}  
matrix unit() {  
    // 返回一个单位矩阵  
    matrix res;  
    for (int i = 0; i < n; ++i) {  
        for (int j = 0; j < n; ++j) {  
            if (i == j) {  
                res.a[i][j] = 1;  
            } else {  
                res.a[i][j] = 0;  
            }  
        }  
    }  
    return res;  
}  
matrix matrix_pow(matrix A, int n, int mod) {  
    // 快速求矩阵 A 的 n 次方  
    matrix res = unit(), temp = A;  
    for (; n; n /= 2) {     //或者写 n>>=1 注意要加等号!  
        if (n & 1) {  
            res = matrix_mul(res, temp, mod);  
        }  
        temp = matrix_mul(temp, temp, mod);  
    }  
    return res;  
}  

【二分图匹配】

#include<iostream>  
#include<bits/stdc++.h>  
using namespace std;  
  
int line[2001][2001];  
int used[2001];  
int boy[2001];  
int n;  
int find(int x)  //第x个女生  
{  
    for(int i=1;i<=n;i++)//遍历男生  
    {  
        if(line[x][i]==1 && used[i]==0)  //x女喜欢i男,且i男没被选  
        {  
            used[i]=1;  //x女生要选i男  
            if(boy[i]==0 || find(boy[i]))  //如果i男单身 or i男名草有主能甩掉前女友(前女友能去递归找到新男友)  
            {  
                boy[i]=x;  //那么i男就跟x女走  
                return 1;  //提示x女找到男朋友了  
            }  
        }  
    }  
    return 0; //x女只能落单  
}  
  
int main()  
{  
    cin>>n;  
    int k,t;  
    for(int i=1;i<=n;i++)  
    {  
        cin>>k;  
        while(k--)  
        {  
            cin>>t;  
            line[i][t]=1;  
        }  
    }  
    int cnt=0;  
    for(int x=1;x<=n;x++)  //女的来挑男的啦!  
    {  
        memset(used,0,sizeof(used)); //关键!表示针对每个女的,所有男的都还没被选过。记住每次都要清零。  
        if(find(x)) cnt++;  
    }  
    cout<<cnt;  
    return 0;  
}  


【差分法+前缀和】

https://blog.csdn.net/m0_38033475/article/details/79758621


【巧用excel日期题】

打开Excel,先把格式调成“日期”,然后就可以加减运算啦!超级爽!(不过求星期几还是得记蔡什么姆的公式)


【尺取法】

https://blog.csdn.net/m0_38033475/article/details/79897291


【贪心】

https://blog.csdn.net/m0_38033475/article/details/79678953

https://blog.csdn.net/m0_38033475/article/details/79760961

https://blog.csdn.net/m0_38033475/article/details/79949081


【博弈】

https://blog.csdn.net/m0_38033475/article/details/79903888


【费马小定理】

https://blog.csdn.net/m0_38033475/article/details/79925115


【diijkstra】

const int MAX_N = 10000;  
const int MAX_M = 100000;  
const int inf = 0x3f3f3f3f;  
struct edge {  
    int v, w, next;  
} e[MAX_M];  
int p[MAX_N], eid, n;  
void mapinit() {  
    memset(p, -1, sizeof(p));  
    eid = 0;  
}  
void insert(int u, int v, int w) {  // 插入带权有向边  
    e[eid].v = v;  
    e[eid].w = w;  
    e[eid].next = p[u];  
    p[u] = eid++;  
}  
void insert2(int u, int v, int w) {  // 插入带权双向边  
    insert(u, v, w);  
    insert(v, u, w);  
}  
  
typedef pair<int, int> PII;  
  
set<PII, less<PII> > min_heap;  
/*用 set 来伪实现一个小根堆,并具有映射二叉堆的功能。堆中 pair<int, int> 的 second 表示顶点下标,first 表示该顶点的 dist 值,注意,只要写上less<PII>就会一first为标准排序,C++,内部实现,不需要管*/  
int dist[MAX_N];  // 存储单源最短路的结果  
bool vst[MAX_N];  // 标记每个顶点是否在集合 U 中  
bool dijkstra(int s) {  
    // 初始化 dist、小根堆和集合 U  
    memset(vst, 0, sizeof(vst));  
    memset(dist, 0x3f, sizeof(dist));  
    min_heap.insert(make_pair(0, s));  
    dist[s] = 0;  
    for (int i = 0; i < n; ++i) {  
        if (min_heap.size() == 0) {  // 如果小根堆中没有可用顶点,说明有顶点无法从源点到达,算法结束  
            return false;  
        }  
        // 获取堆顶元素,并将堆顶元素从堆中删除  
        auto iter = min_heap.begin();   //auto的“指针!”数据类型都用上了,为C++11打call!  
        int v = iter->second;  
        min_heap.erase(*iter);  
        vst[v] = true;  
        // 进行和普通 dijkstra 算法类似的松弛操作  
        for (int j = p[v]; j != -1; j = e[j].next) {  
            int x = e[j].v;  
            if (!vst[x] && dist[v] + e[j].w < dist[x]) {  //对未确定的估计值进行更新。  
                // 先将对应的 pair 从堆中删除,再将更新后的 pair 插入堆  
                min_heap.erase(make_pair(dist[x], x));  
                dist[x] = dist[v] + e[j].w;  
                min_heap.insert(make_pair(dist[x], x));  
            }  
        }  
    }  
    return true;  // 存储单源最短路的结果  
}  

【SPFA】

算法流程:   
使用di表示从源点到顶点i的最短路,额外用一个队列来保存即将进行拓展的顶点列表,并用inqi来标识顶点i是不是在队列中。   
- 初始队列中仅包含源点,且源点s的ds=0 。   
- 取出队列头顶点u,扫描从顶点u出发的每条边,设每条边的另一端为v,边  
  
bool inq[MAX_N];        //判断是否在队列中  
int d[MAX_N];  // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路  
void spfa(int s) {  
    memset(inq, 0, sizeof(inq));        //初始化ing,d      
    memset(d, 0x3f, sizeof(d));  
    d[s] = 0;                           //原点的距离为0,s入队  
    inq[s] = true;  
    queue<int> q;  
    q.push(s);  
    while (!q.empty()) {                //不空时  
        int u = q.front();  
        q.pop();  
        inq[u] = false;                 //出队还要处理一下!                
        for (int i = p[u]; i != -1; i = e[i].next) {//遍历,和以前一样  
            int v = e[i].v;  
            if (d[u] + e[i].w < d[v]) {//更近了  
                d[v] = d[u] + e[i].w;  
                if (!inq[v]) {          //不在队内       
                    q.push(v);  
                    inq[v] = true;  
                }  
            }  
        }  
    }  
}  

【差分约束系统——spfa应用】

https://blog.csdn.net/m0_38033475/article/details/80015141


【floyd】

const int inf = 0x3f3f3f3f;  
int g[MAX_N][MAX_N];  // 算法中的 G 矩阵  
  
// 初始化 g 矩阵  
void init() {  
    for (int i = 0; i < n; ++i) {  
        for (int j = 0; j < n; ++j) {  
            if (i == j) {  
                g[i][j] = 0;  
            } else {  
                g[i][j] = inf;  
            }  
        }  
    }      
}  
  
// 插入一条带权有向边  
void insert(int u, int v, int w) {  
    g[u][v] = w;  
}  
  
// 核心代码  
void floyd() {  
    for (int k = 0; k < n; ++k) {  
        for (int i = 0; i < n; ++i) {  
            for (int j = 0; j < n; ++j) {  
                if (g[i][k] + g[k][j] < g[i][j]) {  
                    g[i][j] = g[i][k] + g[k][j];  
                }  
            }  
        }  
    }      
}  

【kruskal最小生成树】

#include<iostream>  
#include<bits/stdc++.h>  
using namespace std;  
  
const int MAXN=10000;  
const int MAXM=10000;  
struct edge  
{  
    int u,v,w;  
}e[MAXM];  
  
bool cmp(edge a,edge b)  //重要!   
{  
    if(a.w<b.w)  
        return true;  
    else  
        return false;  
}  
//并查集(确定有无回路  
int father[MAXN];  
int get(int a)  
{  
    if(father[a]==a)  
        return a;  
    return father[a]=get(father[a]);  
}  
  
void merge(int a,int b)  
{  
    a=get(a);  
    b=get(b);  
    if(a!=b)  
        father[a]=b;  
}  
  
int main()  
{  
    int n,m;  
    cin>>n>>m;  
    for(int i=1;i<=m;i++)  
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);  
    sort(e+1,e+m+1,cmp);   //把边按边权排序!   
    for(int i=1;i<=n;i++)  
        father[i]=i;  
    int res=0; //记录添加到最小生成树中的边数(最终=n-1即可 !!!   
    int ans=0; //记录边权值和  
    for(int i=1;i<=m && res<n-1;i++)  
    {  
        int u=e[i].u;  
        int v=e[i].v;  
        if(get(u)==get(v)) //若产生回路,不加这条边   
            continue;  
        else //否则加这条边   
        {  
            res++; //记录最小生成树中的边数   
            merge(u,v);  
            ans+=e[i].w;  
        }  
    }  
    cout<<ans;  
    return 0;  
}  


【LCA最近公共祖先】

#include <iostream>  
#include <cstring>  
#include <cstdio>   
using namespace std;  
const int MAX_N=100000;  
const int MAX_M=1000000;  
int isleave[100050];  
struct edge{  
    int v,next;  
}E[MAX_M];  
int p[MAX_N],eid;  
void init(){  
    memset(p,-1,sizeof(p));  
    memset(isleave,0,sizeof(isleave));  
    eid=0;  
}  
void insert(int u,int v){  
    E[eid].v=v;  
    E[eid].next=p[u];  
    p[u]=eid++;  
}  
int d[MAX_N],fa[MAX_N][20];  
void dfs(int u){  
    for(int i=p[u];i!=-1;i=E[i].next){  
        if(d[E[i].v]==-1){  
            d[E[i].v]=d[u]+1;  
            fa[E[i].v][0]=u;  
            dfs(E[i].v);  
        }  
    }  
  
}  
  
int lca(int x,int y){  
    int i,j;  
    if(d[x]<d[y]){  
        swap(x,y);  
    }  
    for(i=0;(1<<i)<=d[x];i++);  
    i--;  
    for(j=i;j>=0;j--){  
        if(d[x]-(1<<j)>=d[y]){  
            x=fa[x][j];  
        }  
    }  
    if(x==y){  
        return x;  
    }  
    for( j=i;j>=0;j--)  
    {  
        if(fa[x][j]!=fa[y][j]){  
            x=fa[x][j];  
            y=fa[y][j];  
        }  
    }  
    return fa[x][0];  
}  
int main() {  
    int n;  
    init();  
    cin>>n;  
    for(int i=0;i<n-1;i++)  
    {  
        int u,v;  
        cin>>u>>v;  
        insert(u,v);  
        insert(v,u);  
        isleave[v]=1;  
    }  
    memset(d,-1,sizeof(d));  
    int root;  
    for(int i=1;i<=n;i++)  
        {  
        if(isleave[i]==0){  
            root=i;  
            break;  
        }  
    }  
    d[root]=1;  
    dfs(root);  
    for(int level=1;(1<<level)<=n;level++){  
        for(int i=1;i<=n;i++){  
            fa[i][level]=fa[fa[i][level-1]][level-1];  
        }  
    }  
    int q;  
    cin>>q;  
    while(q--){  
        int a,b;  
        cin>>a>>b;  
        cout<<lca(a,b)<<endl;  
    }  
    return 0;  
}  

【拓扑排序】

就是要用链式前向星、队列、一个需要根据输入赋值的indegree[MAX_N]数组记录每个节点的入度

struct edge {  
    int v, next;  
} e[MAX_M];  
int p[MAX_N], eid;  
  
int topo() {  
    queue<int> q;  
    for (int i = 1; i <= n; i++) {  
          if (indegree[i] == 0) {  // 将所有入度为零的顶点入队  
            q.push(i);  
        }  
    }  
  
    while (!q.empty()) {  
        int now = q.front();  
        cout << "visiting " << now << endl;  
        q.pop();  
        for (int i = p[now]; i != -1; i = e[i].next) {  
            int v = e[i].v;  
            indegree[v]--;  
            if (indegree[v] == 0) {  // 将入度新变成零的顶点入队  
                q.push(v);  
            }  
        }  
    }  
}  

【欧拉回路】

#include<iostream>  
#include<bits/stdc++.h>  
using namespace std;  
const int MAXN=1010;  
  
vector<int> v[MAXN];  
int N,M;  
int cnt=0;  
void olahuilu(int s)  
{  
    for(auto t=v[s].begin();t!=v[s].end();t=v[s].begin())  
    {  
        int a=*t;  
        v[s].erase(t);  
        v[a].erase(find(v[a].begin(),v[a].end(),s));  
        cnt++;  
        olahuilu(a);  
    }  
    //road[count++]=s;  
}  
int main()  
{  
    cin>>N>>M;  
    int temp=M;  
    while(M--)  
    {  
        int a,b;  
        cin>>a>>b;  
        v[a].push_back(b);  
        v[b].push_back(a);  
    }  
    for(int i=1;i<=N;i++)  
    {  
        if(v[i].size()%2!=0)  
        {  
            cout<<0;  
            return 0;  
        }  
    }  
    olahuilu(1);  //因为无向图找欧拉回路,所以从任意一个起点开始即可  
    if(temp==cnt)   cout<<1;  
    else    cout<<0;  
    return 0;  
}  

【tarjan求最小强连通分量的点个数】

#include<iostream>  
#include<bits/stdc++.h>  
using namespace std;  
const int maxn=2e5+5;  
vector<int> v[maxn];  
int dfn[maxn];  
int low[maxn];  
int cnt=0;  
int vis[maxn]; //1表示在栈中  
stack<int> s;  
int minn=0x3f3f3f3f;  
  
void tarjan(int a)  
{  
    s.push(a);  
    vis[a]=1;  
    dfn[a]=low[a]=++cnt;  
    for(int i=0;i<v[a].size();i++)  
    {  
        int e=v[a][i];  
        if(dfn[e]==0) //该点还没被访问过  
        {  
            tarjan(e);  
            low[a]=min(low[a],low[e]);  
        }   
        else if(vis[e]==1) //被访问过且还在栈中  
        {  
            low[a]=min(low[a],dfn[e]);  
        }   
    }  
    if(dfn[a]==low[a]) //你属于一个强连通分量里   
    {  
        int ans=0; //记录你的这个强连通分量里的点数  
        while(1)  
        {  
            int t=s.top();  
            s.pop();  
            vis[t]=0;  
            ans++;  
            if(t==a)  
                break;  
        }   
        if(ans!=1)  //因为你自己一个点的强连通不符合题意   
            minn=min(minn,ans);  
    }  
}  
int main()  
{  
    int n;  
    cin>>n;  
    for(int i=1;i<=n;i++)  
    {  
        int e;  
        scanf("%d",&e);  
        v[i].push_back(e);  
    }  
    tarjan(1);  
    cout<<minn;  
    return 0;  
}   


【tarjan缩点】

#include<iostream>  
#include<bits/stdc++.h>  
using namespace std;  
const int maxn=1e5;  
vector<int> v[maxn];  
int superpoint[maxn];  
stack<int> s;  
int dfn[maxn],low[maxn];  
int vis[maxn];  
int cnt=0;  
int sum=0;  
void tarjan(int a)  
{  
    vis[a]=1;  
    dfn[a]=low[a]=++cnt;  
    s.push(a);  
    for(int i=0;i<v[a].size();i++)  
    {  
        int e=v[a][i];  
        if(dfn[e]==0)  
        {  
            tarjan(e);  
            low[a]=min(low[a],low[e]);  
        }  
        else if(vis[e]==1)  
        {  
            low[a]=min(low[a],dfn[e]);  
        }  
    }  
    if(low[a]==dfn[a])  
    {  
        sum++; //记录超级点的序号   
        while(1)  
        {  
            int e=s.top();  
            vis[e]=0;  
            s.pop();  
            superpoint[e]=sum;  //记录该点属于哪个超级点   
            if(e==a)  
                break;  
        }  
    }  
}   
  
int chu[maxn];  
int ccnt[maxn];  
int main()  
{  
    int n,m;  
    cin>>n>>m;  
    while(m--)  
    {  
        int a,b;  
        scanf("%d %d",&a,&b);  
        v[a].push_back(b);  
    }  
    tarjan(1);  
    for(int i=1;i<=n;i++)  
    {  
        if(dfn[i]==0)  
            tarjan(i); //这步要记住!因为不一定所有都能由tanrjan(1)可达,有可能有几个点是孤立的小团体~   
    }  
    for(int i=1;i<=n;i++)  
    {  
        for(int j=0;j<v[i].size();j++)  
        {  
            if(superpoint[i]!=superpoint[j])  
            {  
                chu[superpoint[i]]++;  //记录这个超级点的出度   
            }  
        }  
        ccnt[superpoint[i]]++;  //记录这个超级点涵盖了多少个单点   
    }  
      
    int temp=0;  
    int ans;  
    for(int i=1;i<=sum;i++)  
    {  
        if(chu[i]==0)          
        {  
            temp++;  
            ans=ccnt[i];   
        }  
    }  
    if(temp==0 || temp>1)  
        cout<<0;  
    else if(temp==1)  
        cout<<ans;  //如果只有一个超级点满足出度为0,则输出该超级点所涵盖的点的数量,为所求。  
    return 0;  
}  

【dicnic】

const int MAX_N = 100;  // X 集合中的顶点数上限  
const int MAX_M = 10000;  // 总的边数上限  
struct edge {  
    int v, c, next;  // v 是指边的另一个顶点,c 表示容量  
} e[MAX_M];  
int p[MAX_N], eid;  
void init() {  
    memset(p, -1, sizeof(p));  
    eid = 0;  
}  
void insert(int u, int v, int c) {  // 插入一条从 u 连向 v,容量为 c 的弧  
    e[eid].v = v;  
    e[eid].c = c;  
    e[eid].next = p[u];  
    p[u] = eid++;  
}  
void addedge(int u, int v, int c) {  // 用 insert2 来插入网络中的弧  
    insert(u, v, c);  
    insert(v, u, 0);  // 插入一条方向相反、当前容量为 0 的弧  
}  
int S, T;  // S 是源点,T 是汇点  
int d[MAX_N];  // 存储每个顶点的层次  
bool bfs() {  
    memset(d, -1, sizeof(d));  
    queue<int> q;  
    q.push(S);  
    d[S] = 0;  
    while (q.empty()==false) {  
        int u = q.front();  
        q.pop();  
        for (int i = p[u]; i != -1; i = e[i].next) {  
            int v = e[i].v;  
            if (e[i].c > 0 && d[v] == -1) {  
                q.push(v);  
                d[v] = d[u] + 1;  
            }  
        }  
    }  
    return (d[T] != -1);  
}  
  
int dfs(int u, int flow) {  // flow 表示当前搜索分支的流量上限  
    if (u == T) {  
        return flow;  
    }  
    int res = 0;  
    for (int i = p[u]; i != -1; i = e[i].next) {  
        int v = e[i].v;  
        if (e[i].c > 0 && d[u] + 1 == d[v]) {  
            int tmp = dfs(v, min(flow, e[i].c));  // 递归计算顶点 v,用 c(u, v) 来更新当前流量上限  
            flow -= tmp;  
            e[i].c -= tmp;  
            res += tmp;  
            e[i ^ 1].c += tmp;  // 修改反向弧的容量  
            if (flow == 0) {  // 流量达到上限,不必继续搜索了  
                break;  
            }  
        }  
    }  
    if (res == 0) {  // 当前没有经过顶点 u 的可行流,不再搜索顶点 u  
        d[u] = -1;  
    }  
    return res;  
}  
  
int maxflow() {  // 函数返回值就是最大流的结果  
    int res = 0;  
    while (bfs()) {  
        res += dfs(S, INF);  // 初始流量上限为 INF   
    }  
    return res;  
}  

【博弈论】

SG函数 https://blog.csdn.net/m0_38033475/article/details/80272171

SG小结 https://blog.csdn.net/m0_38033475/article/details/80292465


【KMP】

void getnext() {  
    next[1] = 0;               //这里默认字符串都从下标1开始哈!  
    for(int i = 2; i <= n; i++) {  //i从2开始  
        int j = next[i - 1];     
        while(t[j + 1] != t[i] && j > 0) {  
            j = next[j];  
        }  
        if(t[j + 1] == t[i]) {  
            next[i] = j + 1;  
        } else {  
            next[i] = 0;  
        }  
    }  
}  
int kmp() {  
    int j = 0;  
    for(int i = 1; i <= m; i++){  
        while(t[j + 1] != s[i] && j > 0) {   //如果不匹配则一直去赋为next[j]直到匹配或无法匹配  
            j = next[j];  
        }  
        if(t[j + 1] == s[i]) {  
            j++;  
        }  
        if(j >= n) {  
            return i - n + 1;    //第一个匹配的起点  
        }  
    }  
    return 0;  
}  


【拓展KMP】

void getnext() {  
    next[1] = n;  
    int p = 1;  
    while(p < n && t[p] == t[p + 1]) p++;  
    next[2] = p-1;        //对本身的比较来说,先可以把next[1]和next[2]都赋好(求next[2]相当于把字符串向右移一位来比)  
    int k = 2,l;  
    for(int i = 3; i <= n; i++) {  
        p = k + next[k] - 1;  
        l = next[i - k + 1];  
        if (i + l <= p) next[i] = l;  
        else {  
            int j = p - i + 1;  
            if(j < 0)     j = 0;  
            while(i + j <= n && t[i + j] == t[j + 1])     j++;  
            next[i] = j;  
            k = i;  
        }  
    }  
}  

void getextend() {  
    int p = 0;  
    while (p < m && p < n && s[p + 1] == t[p + 1]) {  
        p++;  
    }  
    extend[1] = p;  
    int k = 1, l;  
    for (int i = 2; i <= m; i++) {  
        p = k + extend[k] - 1;  
        l = next[i - k + 1];  
        if (i + l <= p) {  
            extend[i] = l;  
        } else {  
            int j = p - i + 1;  
            if(j < 0) j = 0;  
            while(i + j <= m && j + 1 <=n && s[i + j] == t[j + 1])    j ++;  
            extend[i] = j;  
            k = i;  
        }  
    }  
}  

【字典树】

#include<iostream>  
#include<bits/stdc++.h>  
using namespace std;  
const int maxn=566666;  
int s[maxn][26];  
bool e[maxn];  
int tot=0;  
  
void init()  
{  
    memset(s,-1,sizeof(s));  
    memset(e,false,sizeof(e));  
}   
  
bool insert(char *t,int len) //传入的字符串和其长度   
{  
    int temp=tot;  
    int p=0;  
    for(int i=0;i<len;i++)  
    {  
        if(s[p][t[i]-'a']==-1) s[p][t[i]-'a']=++tot;  
        p=s[p][t[i]-'a'];   //之前这里想当然的写成p=tot,不对!!因为如果没有新开结点呢?  
        if(e[p]==true && tot==temp) return false;  
    }  
    if(tot==temp) return false;  
    e[p]=true;  
    return true;   
}  
  
  
int main()  
{  
    init();  
    int n;  
    cin>>n;  
    bool flag=true;  
    while(n--)  
    {  
        char t[12];  
        scanf("%s",t);  
        if(flag)  
            flag=insert(t,strlen(t));  
    }  
    if(flag==true)  
        cout<<"Good Luck!";  
    else  
        cout<<"Bug!";  
    return 0;  
}  


【树状数组】

int sum(int i)  
{  
    int res=0;  
    while(i>0)  
    {  
        res+=c[i];  
        i-=i&(-i);  
    }  
    return res;  
}  
void add(int i,int v)   
{  
    while(i<=n)  
    {  
        c[i]+=v;  
        i+=i&(-i);  
    }  
}   

【二维树状数组】



【树状数组维护区间最值】



【线段树】

线段树存储区间最大值模板题代码

#include<iostream>  
#include<bits/stdc++.h>  
using namespace std;  
const int maxn=8e5+5;   //注意数组开四倍哦!!!(题中写的:n<=200000)  
int s[maxn];  
  
int renew(int p,int l,int r,int x,int v) //单点修改。注意s保存的是最大值   
{  
    if(l==r) return s[p]=v;  
    int mid=(l+r)/2;  
    if(x<=mid) return s[p]=max(s[p*2+1],renew(p*2,l,mid,x,v));  
    else if(x>mid) return s[p]=max(s[p*2],renew(p*2+1,mid+1,r,x,v));  
}  
  
int getmax(int p,int l,int r,int x,int y)  
{  
    if(x<=l && y>=r) return s[p];  
    int mid=(l+r)/2;  
    int m=0;  
    if(x<=mid) m=max(m,getmax(p*2,l,mid,x,y));  
    if(y>mid)  m=max(m,getmax(p*2+1,mid+1,r,x,y));  
    return m;  
}  
  
int main()  
{  
    int n,m;  
    char a;  
    int b,c;  
    cin>>n>>m;  
    for(int i=1;i<=n;i++)  
    {  
        scanf("%d",&b);  
        renew(1,1,n,i,b);  
    }  
    for(int i=0;i<m;i++)  
    {  
        scanf(" %c",&a);  
        if(a=='Q')  
        {  
            scanf("%d%d",&b,&c);  
            printf("%d\n",getmax(1,1,n,b,c));  
        }  
        else  
        {  
            scanf("%d%d",&b,&c);  
            renew(1,1,n,b,c);  
        }  
    }  
    return 0;  
}  


【线段树区间更新】

求总价值 代码

#include<iostream>  
#include<bits/stdc++.h>  
using namespace std;  
const int maxn=4e5+10;  
int s[maxn];  
int col[maxn]; //保存lazy值   
  
void down(int p,int l,int r)   
{  
    if(col[p]!=0)  
    {  
        int mid=(l+r)/2;  
        s[p*2]=col[p]*(mid-l+1);  
        s[p*2+1]=col[p]*(r-mid);  
        col[p*2]=col[p*2+1]=col[p];  
        col[p]=0;  
    }  
}   
  
void up(int p)  
{  
    s[p]=s[p*2]+s[p*2+1];  
}  
  
void renew(int p,int l,int r,int x,int y,int v)  
{  
    if(x<=l&&y>=r)  
    {  
        s[p]=(r-l+1)*v;  
        col[p]=v;  
        return;  
    }  
    down(p,l,r);  
    int mid=(l+r)/2;  
    if(x<=mid) renew(p*2,l,mid,x,y,v);  
    if(y>mid) renew(p*2+1,mid+1,r,x,y,v);  
    up(p);  
}  
  
/* 
int getsum(int p,int l,int r,int x,int y) 
{ 
    if(x<=l && y>=r) 
        return s[p]; 
    int res=0; 
    int mid=(l+r)/2; 
    if(x<=mid) res+=getsum(p*2,l,mid,x,y); 
    if(y>mid)  res+=getsum(p*2+1,mid+1,r,x,y); 
    return res; 
} */  
  
int main()  
{  
    int n,q;  
    cin>>n>>q;  
    renew(1,1,n,1,n,1);  
    int x,y,z;  
    while(q--)  
    {  
        scanf("%d%d%d",&x,&y,&z);  
        renew(1,1,n,x,y,z);  
    }  
    printf("The total value of the hook is %d.\n",s[1]); //蠢了。。   
    return 0;  
}  



加油!


  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值