最小生成树
kruskal
void kruskal()
{
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
{
x=find(e[i].u),y=find(e[i].v);
if(x!=y){
f[x]=y;tot++;
ans+=e[i].val;
}
}
printf("%d\n",ans);
}
最短路
heap dijkstra
#define MP(x,y) make_pair(x,y)
typedef pair<int,int> pii;
priority_queue<pii,vector<pii>,greater<pii> > q;
void dijkstra(int s)
{
ready(s);
q.push(MP(d[s],s));
while(!q.empty())
{
pii x=q.top();q.pop();
int u=x.second;
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(d[v]>d[u]+e[i].val){
d[v]=d[u]+e[i].val;
q.push(MP(d[v],v));
}
}
}
}
spfa
void spfa(int s)
{
ready(s);
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=false;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(d[v]>d[u]+e[i].val)
{
d[v]=d[u]+e[i].val;
pre[v]=u;//记录从那个点转移过来
pre[v]=i;//记录从那条边转移过来
if(!vis[v]){
vis[v]=true;
q.push(v);
}
}
}
}
}
bfs求权值相同的最短路
int bfs(int s)
{
ready(s);//vis[i]=0,vis[s]=1,d[i]根据情况初始化
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();//只入队一次,不需要vis[u]=0
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(!vis[v])
{
vis[v]=true;
d[v]=d[u]+1;
q.push(v);
}
}
}
}
拓扑排序//可以用栈,队列,优先队列(得到字典序最小的拓扑序列)
void init()
{
memset(head,-1,sizeof(head));
cin>>n>>m;
while(m--)
{
scanf("%d%d",&x,&y);
c[x]++;r[y]++;
add(x,y);
}
for(int i=1;i<=n;i++) if(r[i]==0) q.push(i);
}
void work()
{
while(!q.empty())
{
u=q.top();
q.pop();tot++;
for (int i=head[u];i!=-1;i=e[i].next)
{
v=e[i].to;r[v]--;
if(!r[v]) q.push(v);
}
}
if(tot<n) cout<<"-1";
}
树
对于一棵有n个点的树来说:
会有n-1条边
任意两个点之间有且仅有一条路径
通常来说我们会把树转换成有根树来做(选择一个点当根节点)
如果题目读入的是所有的边,这个时候我们会想了解:
每个点的父亲是谁
每个点的深度
每个点距离根节点的距离
(一些附加信息)比如子树和,子树最大值
倍增lca
void dfs(int u)
{
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(v!=f[u][0]){
f[v][0]=u;
dep[v]=dep[u]+1;
dis[v]=dis[u]+e[i].val;
dfs(v);
}
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=0;i<20;i++)if((dep[x]-dep[y])&(1<<i))x=f[x][i];
if(x==y)return x;
for(int i=19;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
void ready()
{
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
}
int main()
{
dfs(1);
ready();
}
//minn[i][0]=min(value[i],value[fa[i]])
//minn[i][j]=min(minn[i][j-1],minn[fa[i][j-1]][j-1])倍增维护路径的最小权值
树中的预处理
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int M=10005;
int t,n,x,y,z,m;
struct edge{
int to,next,val;
}e[M*10];
int head[M];
int f[M],d[M],v[M]; //依次表示父节点,深度,节点权值
int mx[M],sum1[M],sum2[M];//依次表示子树节点最大值(点权),子树前缀和(点权),根路径前缀和(点权),到根的距离最好用bfs求,这里不写了
void add(int i,int j,int w)
{
e[t].to=j;
e[t].val=w;
e[t].next=head[i];
head[i]=t++;
}
void dfs(int x)
{
d[x]=d[f[x]]+1;
sum2[x]=sum2[f[x]]+v[x];
sum1[x]=v[x];mx[x]=v[x];
for(int i=head[x];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(v!=f[x])
{
f[v]=x;
dfs(v);
sum1[x]+=sum1[v],mx[x]=max(mx[x],mx[v]);
}
}
}
void init()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
for(int i=1;i<=n;i++)
scanf("%d",&v[i]);
}
int main()
{
init();
dfs(1);
for(int i=1;i<=n;i++)
printf("%d %d %d %d %d\n",f[i],d[i],mx[i],sum1[i],sum2[i]);
}
/*
5 4
1 2 1
1 3 1
2 4 1
2 5 1
1 2 3 4 5
根路径前缀和,可以用来求路径节点权值和(配合lca食用)
假如要求x到y路径的权值和,x,y的lca是z。则可以用sum[x]+sum[y]-2sum[z]+value[z]
子树前缀和,可以用来做路径修改(也得配合lca食用)
设定一个修改数组change。如果要对x到y路径上的所有点权值+k,lca为z。那么change[x]+=k,change[y]+=k,change[z]-=k,change[fa[z]]-=k。这样如果最后对change[i]求前缀和的话,最后得到的结果就是i权值的修改量
特点:可以O(1)修改,但是只能一次查询(因为要求前缀和O(n))
*/