最小生成樹
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))
*/