bzoj2753: [SCOI2012]滑雪与时间胶囊

bfs+最小树形图+kruskal算法。

最小树形图形象地来说就是有向图的最小生成树,这个不能拿kruskal算法或者是prim算法直接求,否则会错。

就是w[u][v]!=w[v][u]的情况。

而这道题用朱刘算法肯定是行不通的。

但是这道题的有向边并不是边的性质,而是点的高度决定的。这样我们就可以分层求最小生成树。

如果加进高度为h的点,只需用kruskal算法选最短的边就可以了,而且不会影响到后面的选择。

于是我们把kruskal算法的排序改成以结尾点高度为第一关键字降序和边长度为第二关键字升序排序。。。(意会,要不看cmp,嗯。)

这样就可以辣。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 100000 + 10;
const int maxm = 2000000 + 10;

int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

struct Edge {
    int u,v,d;    
}e[maxm];

int h[maxn],f[maxn];
int g[maxn],v[maxm],next[maxm],eid;
int res1,n,m;
long long res2;
bool vis[maxn];
int q[maxm],l,r,u;

void addedge(int a,int b) {
    v[eid]=b; next[eid]=g[a]; g[a]=eid++;
}

bool cmp(Edge a,Edge b) {
    if(h[a.v]!=h[b.v]) return h[a.v]>h[b.v];
    return a.d<b.d;     
}

int find(int x) {
    return f[x]==x?x:f[x]=find(f[x]);
}    

int main() {
    memset(g,-1,sizeof(g));
    n=read(); m=read();
    for(int i=1;i<=n;i++) h[i]=read();
    for(int i=1,u,v;i<=m;i++) {        
        u=read(); v=read(); e[i].d=read();
        if(h[u]>=h[v]) addedge(u,v);
        if(h[v]>=h[u]) {addedge(v,u); swap(u,v);}
        e[i].u=u; e[i].v=v;
    }
    sort(e+1,e+m+1,cmp);
    
    vis[q[r++]=1]=1,res1=1;
    while(l<r) {
        u=q[l++];
        for(int i=g[u];~i;i=next[i]) if(!vis[v[i]]) {
            vis[q[r++]=v[i]]=1;
            res1++;
        }
    }
    
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1,ru,rv;i<=m;i++) if(vis[e[i].u]&&vis[e[i].v]) {
        ru=find(e[i].u);
        rv=find(e[i].v);
        if(ru!=rv) {
            res2+=e[i].d;
            f[rv]=ru;
        }
    }
    printf("%d %lld\n",res1,res2);
    return 0;
}

posted @ 2016-07-02 20:30  invoid  阅读(325)  评论(0编辑  收藏  举报