[模板] 圖

最小生成樹

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))



*/

發佈了138 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章