• [APIO2008] 免费道路


    [APIO2008]免费道路

    题目大意:这道题就是求一颗生成树,使特殊边的条数恰好为(k)

    第一反应

    考虑朴素的克鲁斯卡尔算法加一个限制,先选鹅卵石路,且选到k个就停止

    带来的问题:

    • 叶子节点特殊处理,都选上(但其实是连通性)

    • 而且你诡异的发现,tm,这个鹅卵石路可以突破最小生成树!!!(不仔细看题面的后果)

    正解

    考虑上文中的连通性,先用水泥路跑一遍(Kruskal),然后不连通的且用到鹅卵石路的都要选上.剩下的既然水泥路可以,那么鹅卵石路也可以代替嘛,先选鹅卵石路,选到(k)个就停止

    emm,那么什么时候是无解呢,有这么几个情况

    • 关键鹅卵石边太多啦,超过(k)
    • 能选鹅卵石边太少了,少于(k)
    • 此图不连通

    代码

    // luogu-judger-enable-o2
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    
    using std::sort;
    
    const int M = 100005;
    const int N = 20005;
    
    int n, m, k, a_cnt;
    int fa[N];
    
    struct Aha{
        int s, t, val, flag;
    }l[M], ans[M];//s,t两个端点, val路性质, flag,联通
    
    inline bool cmp(Aha a, Aha b){
        return a.val < b.val;
    }
    
    int find(int x){
        return x == fa[x] ? x : fa[x] = find(fa[x]);
    }//并茶几没有路径压缩的傻逼就是lmsh7
    
    inline int read(){
        int x = 0, f = 1;
        char c = getchar();
        while(c < '0' || c > '9'){
            if(c == '-')
                f = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9'){
            x = (x << 3) + (x << 1) + (c ^ 48);
            c = getchar();
        }
        return x * f;
    }
    
    int main(){
        n = read(), m = read(), k = read();
        for(int i = 1; i <= n; ++i){
            fa[i] = i;
        }
    
        for(int i = 1; i <= m; ++i){
            l[i].s = read(), l[i].t = read(), l[i].val = read();
            if(l[i].val){
                fa[l[i].s] = find(l[i].s);
                fa[l[i].t] = find(l[i].t);
           
                if(fa[l[i].t] != fa[l[i].s]){
                    fa[fa[l[i].t]] = fa[l[i].s];
                }
            }
        }
    
        for(int i = 1; i <= m; ++i){
            if(!l[i].val && find(l[i].t) != find(l[i].s)){
                fa[fa[l[i].t]] = fa[l[i].s];
                ans[++a_cnt].s = l[i].s;
                ans[a_cnt].t = l[i].t;
                ans[a_cnt].val = l[i].val;//加入答案
                l[i].flag = 1;
                k--;
            }
        }
    
        if(k < 0){
            printf("no solution
    ");//必须要加的边超过了k
            return 0;
        }
        
        fa[1] = find(1);
        
        for(int i = 2; i <= n; ++i){
            fa[i] = find(i);
            if(fa[i] != fa[i - 1]){//不联通
                printf("no solution
    ");
                return 0;
            }
        }
    
        for(int i = 1; i <= n; ++i){
            fa[i] = i;
        }
        
        for(int i = 1; i <= m; ++i){
            if(l[i].flag)
            	fa[fa[l[i].s]] = fa[l[i].t];
        }
        
        sort(l + 1, l + 1 + m, cmp);
        for(int i = 1; i <= m; ++i){
            if((!k && !l[i].val) || l[i].flag) continue;//是割桥或者鹅卵石路且k用完
            
            fa[l[i].s] = find(l[i].s);
            fa[l[i].t] = find(l[i].t);
            if(fa[l[i].t] != fa[l[i].s]){
                fa[fa[l[i].t]] = fa[l[i].s];
                ans[++a_cnt].s = l[i].s;
                ans[a_cnt].t = l[i].t;
                ans[a_cnt].val = l[i].val;
              	if(!l[i].val) k--;   //错误:把这个放在了外面
            }
        }
    
        if(k){
            printf("no solution
    ");
            return 0;
        }
       
        for(int i = 1; i <= a_cnt; ++i){
            printf("%d %d %d
    ", ans[i].s, ans[i].t, ans[i].val);
        }
    
        return 0;
    }
    

    虽然又臭又长,但完全是自己敲出来的啊

    错误

    我都犯了什么错呢,这才是重点啊.在luogu交了7次在bzoj交了37次,才完全明白过来很多地方为什么不可以.

    1. 不可饶恕的错误,并查集忘记路径压缩
    2. 第二遍最小生成树的时候把减小(k)的操作放在了外面
    3. 并查集操作不熟练,导致了判断图的连通性操作失误
    错误代码:
    for(int i = 2; i <= n; ++i){
    	fa[i] = find(i);
        if(fa[i] != fa[i - 1]){
        	printf("no solution
    ");
            return 0;
        }
    }
    
    1. bzoj为啥要求"no solution"带换行啊,以后还是长个心眼
    2. 代码写的丑,调码难炸天

    2018‎-0‎8‎-‎19 ‏‎21:31:23 初稿

  • 相关阅读:
    最长公共子序列
    最长重复子串—后缀数组
    最长递增子序列
    最长重复子串(转)
    最长递增子序列(转)
    最长不重复子串(转)
    连续子数组最大和(转)
    alert 与 console.log
    一个null,让浏览器SB
    javascript实现简单的动画功能
  • 原文地址:https://www.cnblogs.com/LMSH7/p/9502722.html
Copyright © 2020-2023  润新知