图论
最小生成树
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部分略有不同 保存了树结构
感谢大彤大佬的友情check
int 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];
}