hdu4750 最小生成树

关键是利用“所有从s到t的路径上的最长边的最小值”这个条件,我们所要的只是这个最小值:

对于(s,t),这个最小值一定是s到t的最短路上的最长边,则所有可能被取到的边是最小生成树上的边

因为最小生成树利用的就是贪心,树上的边都是图上每两点间的最短路会经过的边

实现:

在kruskal的过程中,添加一条边,权值为w,两个连通分量u和v变为连通,有num[u]*num[v]*2对pair会取到这个w,因为(s,t)和(t,s)不同所以*2

统计,对于一个f,对所有t<=f的guests更新

#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 10010
#define M 500500
struct Edge
{
    int u,v,w;
}edge[M];
bool cmp(Edge u,Edge v)
{
    return u.w<v.w;
}
struct Q
{
    int t,idx,val;
    Q(int t=0,int idx=0,int val=0)
    {
        this->t=t;this->idx=idx;this->val=val;
    }
}q[N*14];
bool cmp2(Q u,Q v)
{
    return u.t<v.t;
}
int n,m,p,fa[N],num[N],ans[N*10];
int _find(int u)
{
    return fa[u]==u?u:fa[u]=_find(fa[u]);
}
void update(int f,int val)
{
    int left=0,right=p,mid;
    while(left<right)
    {
        mid=left+right>>1;
        if(q[mid].t<=f) left=mid+1;
        else right=mid;
    }
    if(q[left].val>f) return;
    q[0].val+=val;
    q[left].val-=val;
}
int main ()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i<m;++i)
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
        scanf("%d",&p);
        for(int i=0;i<p;++i)
        {
            scanf("%d",&q[i].t);
            q[i].idx=i;q[i].val=0;
        }
        sort(q,q+p,cmp2);
        sort(edge,edge+m,cmp);

        for(int i=0;i<n;++i) fa[i]=i,num[i]=1;
        int cc=0;
        for(int i=0;i<m;++i)
        {
            int u=_find(edge[i].u);
            int v=_find(edge[i].v);
            if(u==v) continue;
            update(edge[i].w,num[u]*num[v]*2);
            fa[u]=v;
            num[v]+=num[u];

            if(++cc>=n-1) break;
        }
        for(int i=0,k=0;i<p;++i)
        {
            k+=q[i].val;
            q[i].val=k;
        }
        for(int i=0;i<p;++i) ans[q[i].idx]=q[i].val;
        for(int i=0;i<p;++i) printf("%d\n",ans[i]);
    }
    return 0;
}


你可能感兴趣的:(hdu4750 最小生成树)