zoukankan      html  css  js  c++  java
  • [SCOI2012]滑雪

    2021.1.29

    Solution

    很早之前做过的题……今天又考,还很脑瘫地做错了……但发现之前并没有完全理解,所以还是写一下

    先考虑问题一,求最多能到多少个点。容易知道只要存在一条通路,就一定能到,且后面算费用的时候该点一定能被算到。所以直接从 1 开始 bfs,统计能到的点个数。

    问题二。将能到的所有点和所有边建成新图 (G')。再在该新图上求最小生成树。和常规的生成树不同,该图的边存在有向边,这会影响图的连通性。即如果连了一条边 (u o v)(v)(u) 并不是连通的,然而并查集并不能区分这种差异,这就会造成最后求出的生成树不连通的问题。我们需要对 Kruskal 进行改造,将排序时加入以终点高度为第一关键字降序排,长度为第二关键字升序排。考虑这样的正确性。如果当前正在处理终点高度为 (H) 的边的话,那么高度大于 (H) 的点一定已经处理好了,并且一定是和 (1) 连通的。把这条边加上后,构造出的生成树也一定连通。

    #include<stdio.h>
    #include<queue>
    #include<algorithm>
    using namespace std;
    #define rint register int 
    #define ll long long
    #define N 200007
    #define M N*10
    
    template<class T>
    inline void read(T &x){
        x=0;char c=getchar();T flag=1;
        while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        x*=flag;
    }
    
    int n,m,X[M],Y[M],ans1=0,fa[N],cnt=0,_cnt=0,head[N],_head[N];
    ll dis[M],h[N],ans2=0;
    bool vis[N];
    
    struct E{
        int next,to,rk;
        ll dis;
    }e[M],_e[M];
    queue<int> Q;
    
    inline bool Cmp(const E a,const E b){
        if(h[a.to]!=h[b.to]) return h[a.to]>h[b.to];
        return a.dis<b.dis;
    }
    
    inline int find(int x){
        if(x==fa[x]) return x;
        return fa[x]=find(fa[x]);
    }
    
    inline void add_(int id,int to,ll dis,int rk){
        _e[++_cnt]=(E){_head[id],to,rk,dis};
        _head[id]=_cnt;
    }
    
    inline void add(int id,int to,ll dis,int rk){
        e[++cnt]=(E){head[id],to,rk,dis};
        head[id]=cnt;
    }
    
    inline void bfs(){
        Q.push(1);vis[1]=true;ans1++;
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(rint i=_head[u];i;i=_e[i].next){
                int v=_e[i].to;
                add(u,v,_e[i].dis,_e[i].rk);
                if(!vis[v]){
                    vis[v]=true;ans1++;
                    Q.push(v);
                }
            }
        }
    }
    
    int main(){
        freopen("irrigation.in","r",stdin);
        freopen("irrigation.out","w",stdout); 
        read(n);read(m);
        for(rint i=1;i<=n;++i) read(h[i]),fa[i]=i;
        for(rint i=1;i<=m;++i){
            read(X[i]);read(Y[i]);read(dis[i]);
            if(h[X[i]]>=h[Y[i]]) add_(X[i],Y[i],dis[i],i);
            if(h[X[i]]<=h[Y[i]]) add_(Y[i],X[i],dis[i],i);
        }
        bfs();
        sort(e+1,e+1+cnt,Cmp);
        rint k,u,v;
        for(rint i=1;i<=cnt;++i){
            k=e[i].rk;
            u=X[k],v=Y[k];
            if((!vis[u])||(!vis[v])) continue;
            u=find(u),v=find(v);
            if(u==v) continue;
            fa[u]=v;ans2+=dis[k];
        }
        printf("%d %lld",ans1,ans2);
    }
    
  • 相关阅读:
    非递归前序遍历,效率高 和 中序遍历代码都相似
    递归-变量-堆栈-透彻理解
    Linux 进程管理
    5linux引导流程解析
    Linux 软件包管理
    vim
    Linux常用命令
    Linux 系统安装
    linux 应用和发展
    贴图、纹理、材质的区别是什么? 还有shader
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/14346795.html
Copyright © 2011-2022 走看看