NOIP 模板 跪求大佬们指正错误

来源:互联网 发布:sql存储过程详细教学 编辑:程序博客网 时间:2024/05/22 05:33

  • 图论
    • 最小生成树
      • kruskal
      • prim
    • 最短路
      • 单源spfa
      • 单源Dijkstra
    • 并查集
    • 倍增lca

====================================================

图论

最小生成树

https://www.luogu.org/problem/show?pid=3366#sub

kruskal

路径压缩#include<iostream>#include<cstdio>#include<algorithm>using namespace std;const int N=5000+500,M=200000+500;struct edge{    int s,t,v;}e[M];int fa[N],cnt,n,m;long long ans;bool cmp(edge a,edge b){    return a.v<b.v;}int find(int x){    if(x==fa[x]) return x;    else return fa[x]=find(fa[x]);}void init(){    for(int i=0;i<=n;i++) fa[i]=i;}int main(){    scanf("%d%d",&n,&m);    init();    for(int i=1;i<=m;i++){        scanf("%d%d%d",&e[i].s,&e[i].t,&e[i].v);    }    sort(e+1,e+m+1,cmp);    for(int i=1;i<=m;i++){        int x=find(e[i].s),y=find(e[i].t);        if(x==y) continue;        fa[x]=y;        cnt++;        ans+=e[i].v;        if(cnt==n-1) break;    }    if(cnt!=n-1) printf("orz");    else printf("%lld",ans);    return 0;}

按秩合并 find函数和merge部分略有不同 保存了树结构

感谢大彤大佬的友情checkint find(int x){    if(x==fa[x]) return x;    else return find(fa[x]);}for(int i=1;i<=m;i++){    int x=find(e[i].s),y=find(e[i].t);    if(x==y) continue;    if(rank[x]==rank[y]) rank[y]++;    if(rank[x]>rank[y]) swap(x,y);    fa[x]=y;    cnt++;    ans+=e[i].v;    if(cnt==n-1) break;}

prim

裸的邻接矩阵,记得初始化mp数组maxx

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int N=5000+20;int mp[N][N],d[N];int ans,n,m,a,b,c;bool used[N];void prim(int x){    memset(d,0x3f,sizeof(d));    d[x]=0;    while(1){        int v=-1;        for(int i=1;i<=n;i++){            if(!used[i]&&(v==-1||d[i]<d[v]))            v=i;        }        if(v==-1) return ;        used[v]=1;        ans+=d[v];        for(int i=1;i<=n;i++){            if(d[i]>mp[v][i]){                d[i]=mp[v][i];            }        }    }    return ;}int main(){    memset(mp,0x7f,sizeof(mp));    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++){        scanf("%d%d%d",&a,&b,&c);        mp[a][b]=min(c,mp[a][b]);        mp[b][a]=min(c,mp[b][a]);    }    prim(1);    printf("%d",ans);    return 0;}

堆优化 建双向边记得开两倍数组

#include<iostream>#include<cstdio>#include<queue>#include<cstring>using namespace std;const int N=5000+500,M=200000+500;int cnt,n,m,a,b,c,tot;int first[N],next[M<<1];long long ans;bool used[N];struct node{    int t,v;}e[M<<1];priority_queue<node> Q;bool operator <(node a,node b){    return a.v>b.v;}void build(int f,int t,int v){    e[++tot]=(node){t,v};    next[tot]=first[f];    first[f]=tot;    return ;}void prim(int x){    used[x]=1;    for(int i=first[x];i!=-1;i=next[i]){        Q.push(e[i]);    }    while(!Q.empty()){        node u=Q.top();        Q.pop();        if(used[u.t]) continue;        used[u.t]=1;        ans+=u.v;        cnt++;        if(cnt==n-1) return ;        for(int i=first[u.t];i!=-1;i=next[i]){            if(used[e[i].t]) continue;            Q.push(e[i]);        }    }    return ;}void init(){    memset(first,-1,sizeof(first));}int main(){    init();    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++){        scanf("%d%d%d",&a,&b,&c);        build(a,b,c);build(b,a,c);    }    prim(1);    if(cnt==n-1) printf("%lld",ans);    else printf("orz");    return 0;}

最短路

单源模板 https://www.luogu.org/problem/show?pid=3371

单源spfa

朴素算法,可以在一定情况下将stl中的队列改为栈or手打队列#include<iostream>#include<cstdio>#include<cstring>#include<queue>using namespace std;const int N=10000+50,M=500000+50;int n,m,a,b,c,tot,s;int first[N],next[M],dis[N];bool inq[N];queue<int> Q;struct edge{    int t,v;}e[M];void build(int ff,int tt,int vv){    e[++tot]=(edge){tt,vv};    next[tot]=first[ff];    first[ff]=tot;    return ;}void spfa(int x){    for(int i=1;i<=n;i++) dis[i]=2147483647;    dis[x]=0;    Q.push(x);    inq[x]=1;    while(!Q.empty()){        int u=Q.front();        Q.pop();        inq[u]=0;        for(int i=first[u];i!=-1;i=next[i]){            int v=e[i].t;            if(dis[v]>dis[u]+e[i].v){                dis[v]=dis[u]+e[i].v;                if(!inq[v]){                    Q.push(v);                    inq[v]=1;                }            }        }    }    return ;}int main(){    memset(first,-1,sizeof(first));    scanf("%d%d%d",&n,&m,&s);    for(int i=1;i<=m;i++){        scanf("%d%d%d",&a,&b,&c);        build(a,b,c);    }    spfa(s);    for(int i=1;i<=n;i++) printf("%d ",dis[i]);    return 0;}
玄学优化之双端队列(SLF优化)  应该是能优化时间复杂度,但是stl中的双端队列实现比较慢,慎重使用#include<iostream>#include<cstdio>#include<cstring>#include<deque>using namespace std;const int N=10000+50,M=500000+50;int n,m,a,b,c,tot,s;int first[N],next[M],dis[N];bool inq[N];deque<int> Q;struct edge{    int t,v;}e[M];void build(int ff,int tt,int vv){    e[++tot]=(edge){tt,vv};    next[tot]=first[ff];    first[ff]=tot;    return ;}void spfa(int x){    for(int i=1;i<=n;i++) dis[i]=2147483647;    dis[x]=0;    Q.push_back(x);    inq[x]=1;    while(!Q.empty()){        int u=Q.front();        Q.pop_front();        inq[u]=0;        for(int i=first[u];i!=-1;i=next[i]){            int v=e[i].t;            if(dis[v]>dis[u]+e[i].v){                dis[v]=dis[u]+e[i].v;                if(!inq[v]){                    if(Q.empty()||dis[Q.front()]<dis[v])                    Q.push_back(v);                    else Q.push_front(v);                    inq[v]=1;                }            }        }    }    return ;}int main(){    memset(first,-1,sizeof(first));    scanf("%d%d%d",&n,&m,&s);    for(int i=1;i<=m;i++){        scanf("%d%d%d",&a,&b,&c);        build(a,b,c);    }    spfa(s);    for(int i=1;i<=n;i++) printf("%d ",dis[i]);    return 0;}

单源Dijkstra

如果要求s-t的最短路,在done[t]=1时即可直接返回

堆优化#include<iostream>#include<cstdio>#include<cstring>#include<queue>using namespace std;const int N=10000+50,M=500000+50;int n,m,a,b,c,tot,s;int first[N],next[M],dis[N];bool done[N];struct edge{    int t,v;}e[M];struct node{    int id,dis;};priority_queue<node> Q;bool operator < (node a,node b){    return a.dis>b.dis;}void build(int ff,int tt,int vv){    e[++tot]=(edge){tt,vv};    next[tot]=first[ff];    first[ff]=tot;    return ;}void dij(int x){    for(int i=1;i<=n;i++) dis[i]=2147483647;    Q.push((node){x,0});    dis[x]=0;    while(!Q.empty()){        node u=Q.top();        Q.pop();        if(done[u.id]) continue;        done[u.id]=1;        for(int i=first[u.id];i!=-1;i=next[i]){            int v=e[i].t;            if(dis[v]>dis[u.id]+e[i].v){                dis[v]=dis[u.id]+e[i].v;                Q.push((node){v,dis[v]});            }        }    }    return ;}int main(){    memset(first,-1,sizeof(first));    scanf("%d%d%d",&n,&m,&s);    for(int i=1;i<=m;i++){        scanf("%d%d%d",&a,&b,&c);        build(a,b,c);    }    dij(s);    for(int i=1;i<=n;i++) printf("%d ",dis[i]);    return 0;}

并查集

https://www.luogu.org/problem/show?pid=3367#sub

#include<iostream>#include<cstdio>using namespace std;int fa[20050];int n,m,z,x,y;int find(int x){    if(x==fa[x]) return fa[x];    else return fa[x]=find(fa[x]);}void merge(int x,int y){    x=find(x),y=find(y);    fa[x]=y;}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++) fa[i]=i;    for(int i=1;i<=m;i++){        scanf("%d%d%d",&z,&x,&y);        if(z==1) merge(x,y);        else {            if(find(x)==find(y)) printf("Y\n");            else printf("N\n");        }    }    return 0;}

倍增lca

http://codevs.cn/problem/2370/

核心代码void dfs(int x,int f){//预处理    fa[0][x]=f;    dep[x]=dep[f]+1;    for(int i=fst[x];i!=-1;i=nxt[i]){        int v=e[i].t;        if(v==f) continue;        dis[v]=dis[x]+e[i].v;        dfs(v,x);    }    return ;}void make_lca(){//倍增求父亲    for(int j=1;j<=20;j++){        for(int i=0;i<n;i++)        fa[j][i]=fa[j-1][fa[j-1][i]];    }}int lca(int x,int y){//查询x y的lca    if(dep[x]<dep[y]) swap(x,y);    int dd=dep[x]-dep[y];    for(int j=0;j<=20;j++){        if((1<<j)&(dd)){            x=fa[j][x];        }    }    if(x==y) return x;    for(int j=20;j>=0;j--){        if(fa[j][x]==fa[j][y]) continue;        x=fa[j][x],y=fa[j][y];    }    return fa[0][x];}