OI-板子汇总
2023-10-19 21:25:23 # OI # Note

乱序,想到什么可以补充。

Tarjan 求强连通分量

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
stack<int> s;
vector<int> g[N];
vector<int> g_2[N];
//cscc代表强连通分量的数量,scc代表每个点所属的强连通分量的编号,ins代表是否在栈中
int dfsn[N],low[N],ins[N],scc[N],deep,cscc,n;
void tarjan(int p)
{
    low[p]=dfsn[p]=++deep;
    ins[p]=1;
    s.push(p);
    for(auto &q:g[p])//遍历出边
    {
        if(!dfsn[q])//未访问
        {
            tarjan(q);
            low[p]=min(low[p],low[q]);
        }//访问过且q可达p,因为栈中元素强连通
        else if(ins[q]) low[p]=min(low[p],dfsn[q]);//注意这里为什么是dfsn[q]而不是low[q]
    }
    if(low[p]==dfsn[p])
    {
        int top=0;
        cscc++;
        do
        {
            top=s.top();
            s.pop();
            ins[top]=0;
            scc[top]=cscc;
        }while(top!=p);
        
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)//原图不一定是强连通图,因此每个节点都要看一遍
        if(!dfsn[i])//未访问过
            tarjan(i);
    for(int i=1;i<=n;i++)
        for(auto j:g[i])//以i为起点的出边
            if(scc[i]!=scc[j])//如果不是一个强连通分量,那就可以把两个强连通分量当作两个点连接起来
                g_2[scc[i]].push_back(scc[j]);//j是i的出边,即j的强连通分量是i的强连通分量的出边
    return 0;
}

缩点

P3387 【模板】缩点)

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
const int M=1e5+10;
int a[N];
int n,m,sum,ans=-1;
bool vis[N];
vector<int> e[N];
vector<int> mp[N];
stack<int> s;
bool ins[N];
int low[N],dfsn[N],cnt,cscc,scc[N],val[N],rd[N],dist[N];
void tarjan(int p)
{
    low[p]=dfsn[p]=++cnt;
    ins[p]=true;
    s.push(p);
    for(auto &q:e[p])
    {
        if(!dfsn[q])
        {
            tarjan(q);
            low[p]=min(low[p],low[q]);
        }
        else if(ins[q]) low[p]=min(low[p],dfsn[q]);//在栈里说明可达
    }
    if(low[p]==dfsn[p])
    {
        int top=0;
        cscc++;
        do
        {
            top=s.top();
            s.pop();
            ins[top]=0;
            scc[top]=cscc;
            val[cscc]+=a[top];
        } while (top!=p);
    }
}
void topsort()
{
    queue<int> q;
    for(int i=1;i<=cscc;i++)
        if(!rd[i])
        {
            q.push(i);
            dist[i]=val[i];
        }
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(auto &v:mp[u])
        {
            dist[v]=max(dist[v],dist[u]+val[v]);
            rd[v]--;
            if(!rd[v]) q.push(v);
        }
    }
    for(int i=1;i<=cscc;i++)
        ans=max(ans,dist[i]);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
    }
    for(int i=1;i<=n;i++)
        if(!dfsn[i]) tarjan(i);
    for(int i=1;i<=n;i++)
        for(auto &j:e[i])//j是i的出边的终点
            if(scc[i]!=scc[j])
                mp[scc[i]].push_back(scc[j]),rd[scc[j]]++;
    topsort();
    printf("%d",ans);
    return 0;
}

Tarjan 求割点/割边

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
vector<int> e[N];
int dfsn[N],low[N],fa[N];
int cnt;
vector<int> point;
/*割点*/
void tarjan(int p,bool root=true)
{
    int tot=0;
    low[p]=dfsn[p]=++cnt;
    for(auto &q:e[p])//枚举p的出边
    {
        if(!dfsn[q])//这个点没访问过
        {
            tarjan(q,false);//递归,并且这个点不是根
            low[p]=min(low[p],low[q]);//更新父节点的low值
            tot+=(low[q]>=dfsn[p]);//子节点的low值比父节点的dfs序还要大,那就把需要p的子树个数加一
            //需要p的子树是指删去p后子树无法与外界联系
        }
        else//访问过,那就对low值取min
        {
            low[p]=min(low[p],dfsn[q]);
        }
    }
    if(tot>root)//是根那就要大于1,否则就要大于0
        point.push_back(p);
}
vector<pair<int,int> > bridges;
/*割边*/
void tarjan_0(int p)
{
    low[p]=dfsn[p]=++cnt;
    for(auto &q:e[p])
    {
        if(!dfsn[q])
        {
            fa[q]=p;
            tarjan_0(q);
            low[p]=min(low[p],low[q]);
            if(low[q]>dfsn[p]) bridges.emplace_back(p,q);
        }
        else if(fa[p]!=q) low[p]=min(low[p],dfsn[q]);
    }
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    
}

Tarjan 求点双连通分量

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+100;
const int M=2e6+100;
int n,m,edcnt,cnt,cdcc;
int fir[N],low[N],dfsn[N];
vector<int> dcc[N];
stack<int> s;
vector<int> point;
struct edge
{
    int to,nxt;
}e[M<<1];
void add(int u,int v)
{
    e[++edcnt].to=v;
    e[edcnt].nxt=fir[u];
    fir[u]=edcnt;
}
void tarjan(int p,bool root=true)
{
    int tot=0;
    dfsn[p]=low[p]=++cnt;
    s.push(p);
    if(root&&fir[p]==0)
    {
        dcc[++cdcc].push_back(p);//孤立点
        return;
    }
    for(int i=fir[p];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(!dfsn[v])
        {
            tarjan(v,false);
            low[p]=min(low[v],low[p]);
            if(low[v]>=dfsn[p])
            {
                tot++;
                if(tot>root) point.push_back(p);
                cdcc++;
                int top;
                do
                {
                    top=s.top();
                    s.pop();
                    dcc[cdcc].push_back(top);
                } while (top!=v);
                dcc[cdcc].push_back(p);
            }
        }
        else low[p]=min(low[p],dfsn[v]);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        if(u==v) continue;
        add(u,v);
        add(v,u);
    }
    for(int i=1;i<=n;i++)
        if(!dfsn[i]) tarjan(i);
    printf("%d\n",cdcc);
    for(int i=1;i<=cdcc;i++)
    {
        printf("%d",(int)dcc[i].size());
        for(auto &j:dcc[i])
            printf(" %d",j);
        puts("");
    }
    return 0;
}

Tarjan 求边双连通分量

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
const int M=2e6+10;
int fir[N],low[N],dfsn[N];
int n,m,edcnt=1,cnt,cdcc;
bool bridge[M<<1];
vector<int> dcc[N];
stack<int> s;
struct edge
{
    int to,nxt;
}e[M<<1];
void add(int u,int v)
{
    e[++edcnt].to=v;
    e[edcnt].nxt=fir[u];
    fir[u]=edcnt;
}
void tarjan(int p,int f)
{
    low[p]=dfsn[p]=++cnt;
    s.push(p);
    for(int i=fir[p];i;i=e[i].nxt)
    {
        int q=e[i].to;
        if(!dfsn[q])
        {
            tarjan(q,i);
            low[p]=min(low[p],low[q]);
            if(dfsn[p]<low[q]) bridge[i]=bridge[i^1]=true;
        }
        else if(i!=(f^1)) low[p]=min(low[p],dfsn[q]);//不是反向边
    }
    if(dfsn[p]==low[p])
    {
        cdcc++;
        int top;
        do
        {
            top=s.top();
            s.pop();
            dcc[cdcc].push_back(top);
        } while (top!=p);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        if(u==v) continue;
        add(u,v);
        add(v,u);
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfsn[i]) tarjan(i,0);
    }
    printf("%d\n",cdcc);
    for(int i=1;i<=cdcc;i++)
    {
        printf("%d",(int)dcc[i].size());
        for(auto &j:dcc[i])
            printf(" %d",j);
        puts("");
    }
    return 0;
}

其中利用了异或的性质,偶数加一奇数减一,令初始cnt为1使得边的编号从2开始,这样就可以通过异或1轻松知道反向边的编号了。

最短路

Bellman-Ford

#include<bits/stdc++.h>
using namespace std;
#define maxn 10000007
int n,dist[maxn],m,first[maxn];
int s[maxn],e[maxn],d[maxn];
int main()
{
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&s[i],&e[i],&d[i]);//起点,终点,长度
    }
    memset(dist,0x3f,sizeof(dist));
    dist[1]=0;//起点距离
    for(int i=1;i<n;i++)//最多n-1条边,最多松弛n-1次,因为如果倒着来的话,所有点的dist都未知,每次只能更新一个点(一点点往后推),因此每轮会至少松弛一个点
        for(int j=1;j<=m;j++)//枚举边
        {
            dist[e[j]]=min(dist[e[j]],dist[s[j]]+d[j]);
        }
    return 0;
}

SPFA

Bellman-Ford的队列优化

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000007
int first[maxn],dist[maxn];
bool inque[maxn];
struct edge
{
    int e,next;
    int d;//长度
}ed[maxn];
int cnt;
void add_edge(int u,int v,int w)
{
    cnt++;
    ed[cnt].next=first[u];
    ed[cnt].e=v;
    ed[cnt].d=w;
    first[u]=cnt;
}
void spfa(int s)
{
    memset(dist,0x3f,sizeof(dist));
    dist[s]=0;
    inque[s]=true;//这里不写也可以,因为第一次取出的一定是首点
    queue<int> q;
    q.push(s);//一开始只知道s的最短路
    while(q.size())
    {
        int now=q.front();
        q.pop();
        inque[now]=false;//记录当前点在不在队列里
        for(int i=first[now];i;i=ed[i].next)
        {
            int e=ed[i].e;
            int d=dist[now]+ed[i].d;
            if(d<dist[e])
            {
                dist[e]=d;//如果e变短了,那么也可以用它去更新其他点的最短路
                if(!inque[e])
                {
                    inque[e]=true;
                    q.push(e);
                }
            }
        }
    }
}
int main()
{
    return 0;
}

判断负环

可以记录每个点到起点经过的边数,边数不超过n-1;也可以记录每个点的入队次数,这个次数不能超过n-1。

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000007
int first[maxn],dist[maxn],times[maxn];
bool inque[maxn];
struct edge
{
    int e,next;
    int d;//长度
}ed[maxn];
int cnt;
int T,n,m;
void add_edge(int u,int v,int w)
{
    cnt++;
    ed[cnt].next=first[u];
    ed[cnt].e=v;
    ed[cnt].d=w;
    first[u]=cnt;
}
bool spfa(int s)
{
    memset(times,0,sizeof(times));
    memset(inque,0,sizeof(inque));
    memset(dist,0x3f,sizeof(dist));
    times[s]=1;
    dist[s]=0;
    queue<int> q;
    q.push(s);//一开始只知道s的最短路
    while(q.size())
    {
        int now=q.front();
        q.pop();
        inque[now]=false;//记录当前点在不在队列里
        for(int i=first[now];i;i=ed[i].next)
        {
            int e=ed[i].e;
            int d=dist[now]+ed[i].d;
            if(d<dist[e])
            {
                dist[e]=d;//如果e变短了,那么也可以用它去更新其他点的最短路
                if(!inque[e])
                {
                    inque[e]=true;
                    q.push(e);
                    times[e]++;//记录入队次数
                    if(times[e]>n)//因为有0号点
                        return true;//存在负环
                }
            }
        }
    }
    return false;
}
int main()
{
    
    scanf("%d",&T);
    for(int i=1;i<=T;i++)
    {
        scanf("%d%d",&n,&m);
        for(int j=1;j<=m;j++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            if(c>=0)
            {
                add_edge(a,b,c);
                add_edge(b,a,c);
            }
            else add_edge(a,b,c);
            
        }
        if(spfa(1))
            cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
        memset(first,0,sizeof(first));
    }
    return 0;
}

Dijkstra

P4779 【模板】单源最短路径(标准版))

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000007
int dist[maxn],first[maxn];
int n,m,s;
bool righ[maxn];//这个点有没有放到右边去
struct point{
    int p,d;//原点到p的最短距离为d
    point(){}
    point(int a,int b){p=a;d=b;}
};
bool operator<(const point &a,const point &b)//STL默认大根堆,上面是大的
{
    return a.d>b.d;//小根堆
}
priority_queue<point> heap;
//松弛操作
struct edge
{
    int e,next;
    int d;//长度
}ed[maxn];
int cnt;
void add_edge(int u,int v,int w)
{
    cnt++;
    ed[cnt].next=first[u];
    ed[cnt].e=v;
    ed[cnt].d=w;
    first[u]=cnt;
}
void dijkstra(int s)
{
    memset(dist,0x3f,sizeof(dist));//全部都赋值为最大值
    dist[s]=0;//s到s
    heap.push(point(s,0));
    //可以无变量名
    while(!heap.empty())
    {
        point now=heap.top();
        heap.pop();
        if(righ[now.p]) continue;//判断要写在这里,不能直接判断堆顶,因为可能pop之后堆就空了导致RE
        int p=now.p;
        // int d=now.d;//未加堆优化版,找到目前dist最小的点
        // int p=-1;//代表还没选任何点
        // for(int j=1;j<=n;j++)//循环后就找到了在左边最短路最小的点
        // {
        //     if(!righ[j]&&(p==-1||dist[p]>dist[j]))//righ[]代表有没有放到右边去
        //         p=j;
        righ[p]=1;//放到右边,枚举出边
        for(int j=first[p];j; j=ed[j].next)//ed[j].e即为p的下一个点
        {
            int e=ed[j].e;
            int d=dist[p]+ed[j].d;
            if(dist[e]>d)
            {
                dist[e]=d;
                if(!righ[e]) heap.push(point(e,d));//没访问过并且松弛过的点
            }
        }
    }
}
signed main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add_edge(u,v,w);
    }
    dijkstra(s);
    for(int i=1;i<=n;i++)
    {
        if(dist[i]==1061109567)
        {
            cout<<2147483647<<" ";
            continue;
        }
        printf("%d ",dist[i]);
    }
    return 0;
}

Floyed

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

Johnson

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4 + 10;
const int M = 6e3 + 10;

int n, m, edcnt;
int h[N];
int s[N],t[N],d[N];
int fir[N];
int dist[N];
bool vis[N];

struct edge
{
    int to, val, nxt;
}e[(M << 1) + N];
struct point
{
    int id,len;
    point(){}
    point(int a,int b):id(a),len(b){}
    bool operator < (const point &x)const
    {
        return len > x.len;
    }
};

void Add(int u, int v, int w)
{
    e[++edcnt].nxt = fir[u];
    e[edcnt].to = v;
    e[edcnt].val = w;
    fir[u] = edcnt;
}
void Dijkstra(int s)
{
    memset(dist, 0x3f, sizeof(dist));
    memset(vis, 0, sizeof(vis));
    dist[s] = 0;
    priority_queue<point> q;
    while(!q.empty()) q.pop();
    q.push(point(s,0));
    while(!q.empty())
    {
        point now = q.top();
        q.pop();
        if(vis[now.id]) continue;
        vis[now.id] = true;
        for(int i = fir[now.id]; i; i = e[i].nxt)
        {
            int v = e[i].to;
            int d = dist[now.id] + e[i].val;
            if(d < dist[v])
            {
                dist[v] = d;
                if(!vis[v]) q.push(point(v,dist[v]));
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; ++i)
        scanf("%d%d%d",&s[i],&t[i],&d[i]);
    int tot = m;
    for(int i = 1; i <= n; ++i)
    {
        s[++tot] = 0;
        t[tot] = i;
        d[tot] = 0;
    }
    for(int i = 1; i <= n; ++i) // 多加了一个源点,所以最多松弛 n 次
        for(int j = 1; j <= n + m; ++j) // n+m条边
            h[t[j]] = min(h[t[j]], h[s[j]] + d[j]);
    for(int j = 1; j <= n + m; ++j)
        if(h[t[j]] > h[s[j]] + d[j])
        {
            printf("-1");
            return 0;
        }
    for(int i = 1; i <= m; ++i)
        Add(s[i], t[i], d[i] + h[s[i]] - h[t[i]]);
    for(int i = 1; i <= n; ++i)
    {
        Dijkstra(i);
        ll ans = 0;
        for(int j = 1; j <= n; ++j)
            ans += j * (dist[j] == 0x3f3f3f3f ? 1e9 : dist[j] - h[i] + h[j]);
        printf("%lld\n", ans);
    }
    return 0;
}

倍增求 LCA

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000007
int dist[maxn],first[maxn],f[maxn][20],depth[maxn],g[maxn][20];
int n,m,s;
struct edge
{
    int e,next;
    int d;//长度
}ed[maxn];
int cnt;
void add_edge(int u,int v,int w)
{
    cnt++;
    ed[cnt].next=first[u];
    ed[cnt].e=v;
    ed[cnt].d=w;
    first[u]=cnt;
}
void dfs(int now)//从根节点开始dfs//预处理
{
    for(int i=first[now];i;i=ed[i].next)
        if(ed[i].e!=f[now][0])
        {
            int p=ed[i].e;
            depth[p]=depth[now]+1;
            f[p][0]=now;
            g[p][0]=ed[i].d;//代表从p向上走……步(这里指一步)所经过的最短距离为这条边的边权
            for(int x=1;x<=18;x++)
            {
                f[p][x]=f[f[p][x-1]][x-1];
                g[p][x]=min(g[p][x-1],g[f[p][x-1]][x-1]);
            }
            dfs(p);
        }
}
//1.调整深度2.一起跳
int get_lca(int p1,int p2)//魔改后是求边权最小值
{
	depth[0] = -1; // 防止跳到零号点
    if(depth[p1]<depth[p2]) swap(p1,p2);//保证p1较深
    for(int x=18;x>=0;x--)
    {
        int p=f[p1][x];
        if(depth[p]>=depth[p2])
        {
            p1=p;//完成后p1p2深度就一致了
        }
    }
    if(p1==p2) return p1;//防止p1,p2在同一点上
    for(int x=18;x>=0;x--)
    {
        if(f[p1][x]!=f[p2][x])
        {
            p1=f[p1][x],p2=f[p2][x];
        }
    }
    return f[p1][0];
}

离散化

#include<bits/stdc++.h>
using namespace std;
const int N=500;
struct dat{
    int val,id;
}olda[N];
int newa[N];
bool cmp(dat x,dat y)
{
    return x.val<y.val;
}
int n;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&olda[i].val);
        olda[i].id=i;
    }
    sort(olda+1,olda+1+n,cmp);
    for(int i=1;i<=n;i++)
    {
        newa[olda[i].id]=i;
        if(olda[i].val==olda[i-1].val)
            newa[olda[i].id]=newa[olda[i-1].id];
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d ",newa[i]);
    }
    return 0;
}

高精度

加法

#include<bits/stdc++.h>
using namespace std;
int a[520],b[520],c[520];
int main()
{
    string A,B;
    cin>>A>>B;
    int len=max(A.length(),B.length());
    for(int i=A.length()-1,j=1;i>=0;i--,j++)
        a[j]=A[i]-'0';
    for(int i=B.length()-1,j=1;i>=0;i--,j++)
        b[j]=B[i]-'0';
    for(int i=1;i<=len;i++)
    {
        c[i]+=a[i]+b[i];
        c[i+1]=c[i]/10;
        c[i]%=10;
    }
    if(c[len+1])
        len++;
    for(int i=len;i>=1;i--)
        printf("%d",c[i]);
}

乘法

#include<bits/stdc++.h>
using namespace std;
int a[10200],b[10200],c[10200];
int main()
{
    string A,B;
    cin>>A>>B;
    int lena=A.length(),lenb=B.length();
    for(int i=lena-1;i>=0;i--)
        a[lena-i]=A[i]-'0';
    for(int i=lenb-1;i>=0;i--)
        b[lenb-i]=B[i]-'0';
    for(int i=1;i<=lena;i++)
    {
        for(int j=1;j<=lenb;j++)
        {
            c[i+j-1]+=a[i]*b[j];
        }
    }
    int len=lena+lenb;
    for(int i=1;i<=len;i++)
    {
        c[i+1]+=c[i]/10;
        c[i]%=10;
    }
    while(!c[len])
        len--;
    for(int i=max(1,len);i>=1;i--)
        printf("%d",c[i]);
}

排序

快速排序

#include<bits/stdc++.h>
using namespace std;
int s[200005];
void qsort(int a[],int l,int r)
{
    int i=l,j=r,flag=a[(l+r)/2],tmp;
    do{
        while(a[i]<flag) i++;
        while(a[j]>flag) j--;
        if(i<=j)
        {
            swap(a[i],a[j]);
            // tmp=a[i];
            // a[i]=a[j];
            // a[j]=tmp;
            i++,j--;
        }
    }while(i<=j);
    if(l<j)
        qsort(a,l,j);
    if(i<r)
        qsort(a,i,r);
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d",&s[i]);
    qsort(s,1,n);
    for(int i=1;i<=n;i++)
    {
        printf("%d ",s[i]);
    }
}

拓扑排序

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,m;
int h[N],e[N],nex[N],cnt;
int que[N],d[N];
void add(int a,int b)//他是从0~m-1
{
    e[++cnt]=b;//end
    nex[cnt]=h[a];
    h[a]=cnt;
}
bool topsort()
{
    int hh=1,tt=0;
    for(int l=1;l<=n;l++)
        if(!d[l])
            que[++tt]=l;
        while(hh<=tt)
        {
            int t=que[hh++];//取出队首
            for(int i=h[t];i!=-1;i=nex[i])
            {
                int j=e[i];
                d[j]--;
                if(d[j]==0)
                    que[++tt]=j;
            }
        }
    return tt==n;
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof(h));
    for(int i=1;i<=m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        d[b]++;
    }
    if(topsort())
    {
        for(int i=1;i<=n;i++)
            printf("%d ",que[i]);
        puts("");
    }
    else
        cout<<-1<<endl;
    return 0;
}

最小生成树

Kruscal

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+7;
int n,m;
int fa[N];
struct edge{
	int u,v,w;//u-----w------>v
}eg[1000005]; 
bool cmp(edge a,edge b)
{
	return a.w<b.w;
}
int cnt,ans,tot;
void add(int u,int v,int w)
{
	eg[++cnt].u=u;
	eg[cnt].v=v;
	eg[cnt].w=w;
}
int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
void Kruscal()
{
	sort(eg+1,eg+m+1,cmp);
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		int rootu=find(eg[i].u);
		int rootv=find(eg[i].v);
		if(rootu!=rootv)
		{
			ans+=eg[i].w;
			tot++;
			fa[rootu]=rootv;
		}
	}
	return;
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&eg[i].u,&eg[i].v,&eg[i].w);
 	return 0;
}

Prim

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000007
int dist[maxn],first[maxn];
int n,m,s;
bool righ[maxn];//这个点有没有放到右边去
struct point{
    int p,d;//原点到p的最短距离为d
    point(){}
    point(int a,int b){p=a;d=b;}
};
bool operator<(const point &a,const point &b)//STL默认大根堆,上面是大的
{
    return a.d>b.d;
}
priority_queue<point> heap;
//松弛操作
struct edge
{
    int e,next;
    int d;//长度
}ed[maxn];
int cnt;
void add_edge(int u,int v,int w)
{
    cnt++;
    ed[cnt].next=first[u];
    ed[cnt].e=v;
    ed[cnt].d=w;
    first[u]=cnt;
}
void prim(int s)
{
    memset(dist,0x3f,sizeof(dist));//全部都赋值为最大值
    dist[s]=0;//s到s
    heap.push(point(s,0));
    while(!heap.empty())
    {
        point now=heap.top();
        heap.pop();
        int p=now.p;
        if(righ[p]) continue;
        righ[p]=1;//放到右边
        for(int j=first[p];j;j=ed[j].next)//ed[j].e即为p的下一个点
        {
            int e=ed[j].e;
            int d=ed[j].d;
            if(dist[e]>d)
            {
                dist[e]=d;
                if(!righ[e]) heap.push(point(e,d));
            }
        }
    }
}
signed main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add_edge(u,v,w);
    } 
    return 0;
}

莫队

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+8;
int q,a[N],cnt[N],l=1,r=0,now=0,belong[N];
struct query
{
    int l,r;
}e[N];
int cmp(query a, query b)
{
	return (belong[a.l]^belong[b.l])?belong[a.l]<belong[b.l]:((belong[a.l]&1)?a.r<b.r:a.r>b.r);
}
void add(int pos)
{
    if(!cnt[a[pos]]) ++now;
    ++cnt[a[pos]];
}
void del(int pos)
{
    --cnt[a[pos]];
    if(!cnt[a[pos]]) --now;
}
void work()
{
    for(int i=1;i<=q;i++)
    {

        int ql,qr;
        scanf("%d%d",&qr,&ql);
        while(l<ql) del(l++);
        while(l>ql) add(--l);
        while(r<qr) add(++r);
        while(r>qr) del(r--);
        printf("%d",now);
    }
}
int main()
{

    return 0;
}

快读

int

inline int read()
{
	int x=0,y=1; 
	char c=getchar();
	while(c>'9'||c<'0')
	{
		if(c=='-') y=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=x*10+c-'0';
		c=getchar();
	}
	return x*y;
}

double

inline double read()
{
	double x=0,y=1,w=1;
	char c=getchar();
	while(c>'9'||c<'0')
	{
		if(c=='-') y=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=x*10+c-'0';
		c=getchar();
	}
	if(c=='.')
	{
		c=getchar()
		while(c>='0'&&c<='9')
		{
			w=w/10.0;
			x+=(c-'0')*w;
			c=getchar();
		}
	}
	return x*y;
}

线段树

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
typedef long long ll;
int a[N];
struct node
{
    int l,r;
    ll add,sum;
}tr[N<<2];
void pushup(int u)//上传
{
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void pushdown(int u)//下放
{
    node &root=tr[u],&left=tr[u<<1],&righ=tr[u<<1|1];
    if(root.add)
    {
        left.add+=root.add;
        left.sum+=1ll*(left.r-left.l+1)*root.add;
        righ.add+=root.add;
        righ.sum+=1ll*(righ.r-righ.l+1)*root.add;
        root.add=0;
    }
}
void build(int u,int l,int r)//从u开始递归,建l~r的树,一般为build(1,1,n)
{
    tr[u].l=l,tr[u].r=r;
    if(l==r)
    {
        tr[u].sum=a[r];
        tr[u].add=0;
        return;
    }
    int mid=(l+r)>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    pushup(u);
}
void modify(int u,int l,int r,int d)//从u开始递归,查询区间,加上d
{
    if(tr[u].l>=l&&tr[u].r<=r)
    {
        tr[u].sum+=1ll*(tr[u].r-tr[u].l+1)*d;
        tr[u].add+=d;
        return;
    }
    pushdown(u);
    int mid=(tr[u].l+tr[u].r)>>1;
    if(l<=mid) modify(u<<1,l,r,d);
    if(r>mid) modify(u<<1|1,l,r,d);
    pushup(u);
}
ll query(int u,int l,int r)//从u开始递归,查询l,r之间的区间和
{
    if(tr[u].l>=l&&tr[u].r<=r) return tr[u].sum;
    pushdown(u);
    int mid=(tr[u].l+tr[u].r)>>1;
    ll sum=0;
    if(l<=mid) sum+=query(u<<1,l,r);
    if(r>mid) sum+=query(u<<1|1,l,r);
    return sum;
}
int main()
{

    return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;

int n, m, q;
int a[N];
struct Node
{
    int l, r;
    LL sum, add, mul;
}tr[N << 2];

void pushup(int u)
{
    tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % q;
}
void eval(Node &t, LL add, LL mul)
{
    t.sum = (t.sum * mul % q + (t.r - t.l + 1) * add % q);
    t.add = (t.add * mul % q + add) % q;
    t.mul = t.mul * mul % q;
}
void pushdown(int u)
{
    eval(tr[u << 1], tr[u].add, tr[u].mul);
    eval(tr[u << 1 | 1], tr[u].add, tr[u].mul);
    tr[u].add = 0;
    tr[u].mul = 1;
}
void build(int u, int l, int r)
{
    tr[u].l = l, tr[u].r = r, tr[u].add = 0, tr[u].mul = 1;
    if(l == r)
    {
        tr[u].sum = a[r];
        return;
    }
    int mid = (l + r) >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
}
void modify(int u, int l, int r, int add, int mul)
{
    if(tr[u].l >= l && tr[u].r <= r)
    {
        eval(tr[u], add, mul);
        return;
    }
    pushdown(u);
    int mid = (tr[u].l + tr[u].r) >> 1;
    if(l <= mid) modify(u << 1, l, r, add, mul);
    if(r > mid) modify(u << 1 | 1, l, r, add, mul);
    pushup(u);
}
LL query(int u, int l, int r)
{
    if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
    pushdown(u);
    int mid = (tr[u].l + tr[u].r) >> 1;
    LL sum = 0;
    if(l <= mid) sum = (sum + query(u << 1, l, r)) % q;
    if(r > mid) sum = (sum + query(u << 1 | 1, l, r)) % q;
    return sum;
}

int main()
{
    scanf("%d%d%d", &n, &m, &q);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    build(1, 1, n);
    while(m--)
    {
        int op, l, r, k;
        scanf("%d%d%d", &op, &l, &r);
        if(op == 1)
        {
            scanf("%d", &k);
            modify(1, l, r, 0, k);
        }
        else if(op == 2)
        {
            scanf("%d", &k);
            modify(1, l, r, k, 1);
        }
        else printf("%lld\n", query(1, l, r));
    }
    return 0;
}

可持久化

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;

int n, m, cnt;
int idx;
int a[N];
int root[N];
struct Node
{
    int l, r;
    int key;
}tr[23 * N];

void build(int &u, int l, int r)
{
    u = ++idx;
    if(l == r)
    {
        tr[u].key = a[r];
        return;
    }
    int mid = (l + r) >> 1;
    build(tr[u].l, l, mid);
    build(tr[u].r, mid + 1, r);
}
void modify(int &u, int p, int l, int r, int x, int v)
{
    u = ++idx;
    tr[u] = tr[p];
    if(l == r)
    {
        tr[u].key = v;
        return;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) modify(tr[u].l, tr[p].l, l, mid, x, v);
    else modify(tr[u].r, tr[p].r, mid + 1, r, x, v);
}
int query(int u, int l, int r, int x)
{
    if(!u) return 0;
    if(l == r) return tr[u].key;
    int mid = (l + r) >> 1;
    if(x <= mid) return query(tr[u].l, l, mid, x);
    if(x > mid) return query(tr[u].r, mid + 1, r, x);
    return 0;
}

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    build(root[0], 1, n);
    while(m--)
    {
        int op, last, loc, val;
        scanf("%d%d", &last, &op);
        if(op == 1)
        {
            scanf("%d%d", &loc, &val);
            modify(root[++cnt], root[last], 1, n, loc, val);
        }
        else if(op == 2)
        {
            scanf("%d", &val);
            printf("%d\n", query(root[last], 1, n, val));
            root[++cnt] = root[last];
        }
    }
    return 0;
}

平衡树

Treap

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;
const int INF = 1e8;

int n, root, idx;
struct Node
{
    int l, r, key, val;
    int cnt, siz;
}tr[N];

int get_node(int key)
{
    tr[++idx].key = key;
    tr[idx].val = rand();
    tr[idx].cnt = tr[idx].siz = 1;
    return idx;
}
void pushup(int p)
{
    tr[p].siz = tr[tr[p].l].siz + tr[tr[p].r].siz + tr[p].cnt;
}
void build()
{
    root = get_node(-INF);
    tr[root].r = get_node(INF);
    pushup(root);
}
void zig(int &p)
{
    int q = tr[p].l;
    tr[p].l = tr[q].r;
    tr[q].r = p;
    p = q;
    pushup(tr[p].r);
    pushup(p);
}
void zag(int &p)
{
    int q = tr[p].r;
    tr[p].r = tr[q].l;
    tr[q].l = p;
    p = q;
    pushup(tr[p].l);
    pushup(p);
}
void insert(int &p, int key)
{
    if(!p) p = get_node(key);
    else if(tr[p].key == key) tr[p].cnt++;
    else if(tr[p].key > key)
    {
        insert(tr[p].l, key);
        if(tr[tr[p].l].val > tr[p].val) zig(p);
    }
    else 
    {
        insert(tr[p].r, key);
        if(tr[tr[p].r].val > tr[p].val) zag(p);
    }
    pushup(p);
}
void remove(int &p, int key)
{
    if(!p) return;
    if(tr[p].key == key)
    {
        if(tr[p].cnt > 1) tr[p].cnt--;
        else if(tr[p].l || tr[p].r)
        {
            if(!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val)
            {
                zig(p);
                remove(tr[p].r, key);
            }
            else
            {
                zag(p);
                remove(tr[p].l, key);
            }
        }
        else p = 0;
    }
    else if(tr[p].key > key) remove(tr[p].l, key);
    else remove(tr[p].r, key);
    pushup(p);
}
int get_rank(int p, int key)
{
    if(!p) return 1;
    if(tr[p].key == key) return tr[tr[p].l].siz + 1;
    if(tr[p].key > key) return get_rank(tr[p].l, key);
    return tr[tr[p].l].siz + tr[p].cnt + get_rank(tr[p].r, key);
}
int get_key(int p, int rank)
{
    if(!p) return INF;
    if(tr[tr[p].l].siz >= rank) return get_key(tr[p].l, rank);
    if(tr[tr[p].l].siz + tr[p].cnt >= rank) return tr[p].key;
    return get_key(tr[p].r, rank - tr[tr[p].l].siz - tr[p].cnt);
}
int get_prev(int p, int key)
{
    if(!p) return -INF;
    if(tr[p].key >= key) return get_prev(tr[p].l, key);
    return max(tr[p].key, get_prev(tr[p].r, key));
}
int get_next(int p, int key)
{
    if(!p) return INF;
    if(tr[p].key <= key) return get_next(tr[p].r, key);
    return min(tr[p].key, get_next(tr[p].l, key));
}

int main()
{
    build();
    scanf("%d", &n);
    while(n--)
    {
        int op, x;
        scanf("%d%d", &op, &x);
        switch(op)
        {
            case 1: 
                insert(root, x);
                break;
            case 2:
                remove(root, x);
                break;
            case 3: 
                printf("%d\n", get_rank(root, x) - 1);
                break;
            case 4:
                printf("%d\n", get_key(root, x + 1));
                break;
            case 5:
                printf("%d\n", get_prev(root, x));
                break;
            case 6:
                printf("%d\n", get_next(root, x));
                break;
        }
    }
    return 0;
}

字符串

KMP

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char a[N],b[N];
int nxt[N];
int la,lb;
void calc()
{
    for(int i=1,j=0;i<lb;i++)
    {
        while(j&&b[i]!=b[j]) j=nxt[j-1];
        if(b[i]==b[j]) j++;
        nxt[i]=j;
    }
}
void kmp()
{
    for(int i=0,j=0;i<la;i++)
    {
        while(j&&a[i]!=b[j]) j=nxt[j-1];
        if(a[i]==b[j]) j++;
        if(j==lb)
        {
            printf("%d\n",i-j+2);
            j=nxt[j-1];
        }
    }
}
int main()
{
    scanf("%s%s",a,b);
    la=strlen(a);
    lb=strlen(b);
    calc();
    kmp();
    for(int i=0;i<lb;i++)
        printf("%d ",nxt[i]);
    return 0;
}

Z

#include<bits/stdc++.h>
using namespace std;
char s[1000005];
int n;
int z[100005];
int main()
{
    n=strlen(s);
    z[0]=n;
    for(int i=1,l=0,r=0;i<n;i++)
    {
        if(z[i-l]<r-i+1)//当前已经相同的部分比已知lcp要大
            z[i]=z[i-l];
        else
        {
            z[i]=max(r-i+1,0);
            while(i+z[i]<n&&s[z[i]]==s[i+z[i]]) z[i]++;
            l=i,r=i+z[i]-1;
        }
    }
    return 0;
}

Manacher

#include<bits/stdc++.h>
using namespace std;
int n;
int d[1000005];
char s[1000005];
int main()
{
    for(int i=0,l=0,r=-1;i<n;i++)
    {
        int j=l+r-i;//对称点
        int dj=j>=0?d[j]:0;//不能越界
        d[i]=max(min(dj,r-i+1),0);
        if(j-dj<l)//j-dj+1<=l
        {
            while(i-d[i]>=0&&i+d[i]<n&&s[i-d[i]]==s[i+d[i]]) d[i]++;
            l=i-d[i]+1,r=i+d[i]-1;
        }
    }
    return 0;
}

Trie

数学

快速幂

非递归:

int ksm(int x, int y)
{
    int res = 1;
    while(y > 0)
    {
        if(y & 1) res = res * x;
        x = x * x;
        y >>= 1;
    }
    return res;
}

递归:

int ksm(int x, int y)
{
    if(y == 1) return x;
    int z = ksm(x, (y >> 1));
    z = z * z;
    if(y & 1) z = z * x;
    return z;
}

素性测试

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int prime[] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};

int n;
ll Ksm(int x, int y, int p)
{
    ll res = 1;
    while(y > 0)
    {
        if(y & 1) res = res * x % p;
        x = x * x % p;
        y >>= 1;
    }
    return res;
}
bool Miller_Rubbin(int n, int a)
{
    int d = n - 1, r = 0;
    while(!(d & 1))
    {
        d >>= 1;
        r++;
    }
    int x = Ksm(a, d, n);
    if(x == 1) return true;
    for(int i = 1; i <= r; ++i)
    {
        if(x == n - 1) return true;
        x = 1ll * x * x % n;
    }
    return false;
}
bool Check(int x)
{
    if(n < 2) return false;
    for(int i = 1; i <= 12; ++i)
    {
        if(!Miller_Rubbin(x, prime[i])) return false;
    }
    return true;
}
int main()
{
    int t;
    scanf("%d", &t);
    cout << Check(t) << endl;
    return 0;
}