noip 2013 货车运输

这个题让我对最小生成树和倍增有了新的理解,这里不得不感谢两个人,吕欣和陈弘毅,聪明的两个人,没有你们的讲解,我始终不明白

难点之一:最大生成树

难点之二:什么时候该添加邻接表和前向星

最大难点:如果使用倍增来记树上路径的最值

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 10010
#define MAXM 50010
int ecnt,head[MAXN],deep[MAXN],f[MAXN][30],dis[MAXN],n,m,fat[MAXN],q,jl[MAXN][30];
bool vis[MAXN];
struct node
{
    int fro,next,to,dis;    
}e[4*MAXN+5],ed[MAXM];
void Add_edge(int from,int to,int dis)
{
    e[++ecnt].next=head[from];
    e[ecnt].to=to;
    e[ecnt].dis=dis;
    head[from]=ecnt;
}
void dfs(int u) //更新深度及dis值
{
    vis[u]=1;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        int d=e[i].dis;
        if(!vis[v])
        {
            deep[v]=deep[u]+1;
            dis[v]=dis[u]+d;
            f[v][0]=u;
            jl[v][0]=e[i].dis;
            dfs(v);
        }
    }
}

void init() //i的2^j祖先就是i的2^(j-1)祖先的 2^(j-1)祖先
{
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i<=n;i++)
        {
            if(f[i][j-1])
            {
                f[i][j]=f[f[i][j-1]][j-1];
                jl[i][j]=min(jl[i][j-1],jl[f[i][j-1]][j-1]);//在倍增的过程中找距离 
            }
        }   
}

int LCA(int a,int b)
{
    if(deep[a]<deep[b]) swap(a,b); //使a成为深度较深的那个点
    int i;
    for(i=0;(1<<i)<=deep[a];i++);
    i--;
    for(int j=i;j>=0;j--) //使a,b深度相同
    {
        if(deep[a]-(1<<j)>=deep[b])
            a=f[a][j];
    }
    if(a==b) return a; //同一层同一节点
    for(int j=i;j>=0;j--)//一直向上找,找到两个节点不同的最靠上的那一个
    {
        if(f[a][j]&&f[a][j]!=f[b][j])
        {
            a=f[a][j];
            b=f[b][j];
        }
    }
    return f[a][0];//返回他们的父亲
}

int father(int x)
{
    if(fat[x]!=x) fat[x]=father(fat[x]);
    return fat[x];
}

int unio(int x,int y)
{
    int fx=father(x);
    int fy=father(y);
    if(fx!=fy) fat[fy]=fx;
}

int cmp(const node &a,const node &b)
{
    if(a.dis>b.dis) return 1;
    else return 0;
}

int query(int s,int s1)
{
    int mn=0x7f7f7f7f;
    int t=deep[s]-deep[s1];//t的主要作用是保证他们不在同一层 
    for(int i=0;(1<<i)<=n;i++)
    {
        if(t&(1<<i))
        {
           mn=min(mn,jl[s][i]);//一直往上移动 
           s=f[s][i];
        }
    }
    return mn;
    
}
int main()
{
    freopen("truck.in","r",stdin);
    freopen("truck.out","w",stdout);
    scanf("%d%d",&n,&m);
    int a,b,c,k=0;
    for(int i=1;i<=n;i++)
        fat[i]=i;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        ed[i].fro=a,ed[i].to=b,ed[i].dis=c;    
    }
    sort(ed+1,ed+1+m,cmp);
    for(int i=1;i<=m;i++)
    {
        if(father(ed[i].fro)!=father(ed[i].to))
        {
            Add_edge(ed[i].fro,ed[i].to,ed[i].dis);
            Add_edge(ed[i].to,ed[i].fro,ed[i].dis);
            unio(ed[i].fro,ed[i].to);
            k++;
        }
        if(k==n-1) break;
    }
    for(int i=1;i<=n;i++)
        if(!vis[i]) dfs(i);
    init();
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&a,&b);
        if(father(a)==father(b)) {
            int s=LCA(a,b);
            cout<<min(query(a,s),query(b,s))<<endl;
        }
        else cout<<"-1"<<endl; 
    }    
}

/**
LCA(a,b)
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3


5 7
4 3 4440
3 1 22348
1 3 28368
2 4 25086
5 3 6991
4 3 10638
3 1 11106
4
4 5
1 3
5 4
2 5
6991
28368
6991
6991


**/

 

posted @ 2017-06-12 18:48  xinyimama  阅读(104)  评论(0)    收藏  举报