zoukankan      html  css  js  c++  java
  • 题解 P6722 【「MCOI-01」Village 村庄】

    Step1 前置芝士:

    如何证明一个图是二分图,又如何证明一个图不是二分图?

    从定义入手: 二分图是一种不含有奇数条边环的图。

    所以,对于此题,如果我们能确定新图中是否含有基环,我们就相应的可以确定这个数据合不合法。

    Step2 从暴力开始

    考虑如何暴力建新图?

    如果$dis_{u, v} ge k$,那么在新图中就可以有连边呗。

    所以,求出全源最短路,然后暴力建新图,最后判断是否为二分图就是我们的暴力做法了。
    (~~感觉这个暴力也很麻烦的亚子,还不如继续推写正解(滑稽~~)

    Step 3 考虑基环的性质

    在一个基环上,我们考虑每个点的编号为$a_1, a_2, a_3$ ~ $a_m$。
    其中$a_m$为编号最大的一个基环上的点, 令 $m > 3$。

    假设基环上不存在一个点$a_p$, 满足$dis(a_1, a_p) ge k$ ^ $dis(a_p, a_m) ge k$,即不满足存在三元环的条件


    不妨利用数学归纳法:
    设一点$a_x$存在于基环上。

    当 $x = 1$时,显然有 $dis(a_1, a_m) ge k$ ^ $dis(a_1, a_2) ge k$

    当$x = 2$时,显然有 $dis(a_2, a_3) ge k$ ^ $dis(a_2, a_m) < k$

    当$x = 2y(y ≠0)$时,有 $dis(a_{2y}, a_{2y+1}) ge k$ ^ $dis(a_{2y},a_1) < k$ ^ $dis(a_{2y}, a_m) < k$ $Rightarrow$ 可知,此时的$a_m$不能存在于基环中,显然不对。

    (这里还要结合原图来看啊,建议大家结合样例来手摸。)


    (基环)


    那么,证明了图中存在三元环之后,~~我们来看看官方题解~~,看看三元环跟树的直径的关系。

    假设图中1,2,3号点构成三元环。
    那么,有$d + e ge k$, $d + f ge k$, $e + f ge k$.

    令图中$a + b$为极大值,则 $4,5,6$为树的直径。

    假设不存在一点到直径两端点可以构成三元环。

    那么,令1号点等效于1,2,3号点。

    为满足条件,我们有:

    $d + c + a < k$, 配合前面的柿子:
    $d + e ge k$

    不等式基本原则,$a + c < e$

    所以 $b + c + e > b + c + a + c > a + b$,此时$(2,6)$成为直径,不符合原图。

    所以,三元环上一定有一点到直径的两端距离均大于$k$。于是,我们只要找出树的直径,然后判断是否有点到其两端的距离均$ge k$即可。如果存在,则原图存在基环,不成立。

    #include <bits/stdc++.h>
    using namespace std;
    #define N 100010
    
    template <class T>
    inline void read(T& a){
        T x = 0, s = 1;
        char c = getchar();
        while(!isdigit(c)){
            if(c == '-') s = -1;
            c = getchar();
        }
        while(isdigit(c)){
            x = x * 10 + (c ^ '0');
            c = getchar();
        }
        a = x * s;
        return ;
    }
    
    struct node{      
        public:
            int v, w, next;
        
        public:
            node(int v = 0, int w = 0, int next = 0){
                this -> v = v;
                this -> w = w;
                this -> next = next;
                return ;
            }// 为什么存个边要写得这么毒瘤?  因为方便赋予初值啊 
            
        public: 
            inline void clean(){
                this -> v = 0;
                this -> next = 0;
                this -> w = 0;
                return ;
            }
        
    } t[N << 1];
    int f[N];
    
    int bian = 0;
    inline void add(int u, int v, int w){
        t[++bian] = node(v, w, f[u]), f[u] = bian;
        t[++bian] = node(u, w, f[v]), f[v] = bian;
        return ;
    }
    
    int dis[N], dis1[N], dis2[N];
    int s1, s2;
    int maxn = -666;
    int cur = 0;
    
    #define v t[i].v
    void dfs(int now, int father, int* dis){  // 用指针方便传入dis数组 
        if(dis[now] > maxn){
            maxn = dis[now];
            cur = now;
        }    
        for(int i = f[now]; i; i = t[i].next){
            if(v != father){
                dis[v] = dis[now] + t[i].w;
                dfs(v, now, dis);
            }
        }
        return ;
    }
    #undef v
    
    inline void search(int x, int& y, int* dis){   // 找端点等各种操作 
        maxn = -666;
        cur = 0;
        dis[x] = 0;
        dfs(x, 0, dis);
        y = cur;
        return ;
    }
    
    inline void clean(){   // 不要忘记清空 
        for(int i = 1;i <= bian; i++)
            t[i].clean();
        memset(f, 0, sizeof(f));
        bian = 0;
        return ;
    }
    
    int main(){
        int T;
        read(T);
        while(T--){
            clean();
            int n, k;
            read(n), read(k);
            s1 = 0, s2 = 0;
            for(int i = 1; i < n; i++){
                int x, y, w;
                read(x), read(y), read(w); 
                add(x, y, w);
            }
            search(1, s1, dis);
            search(s1, s2, dis1);  // 找端点,顺便记录dis1 
            int temp = 0;
            search(s2, temp, dis2);  // 多搜一次,找dis2 
            bool flag = 0;
            for(int i = 1;i <= n; i++)
                if(dis1[i] >= k && dis2[i] >= k) {
                    flag = 1;
                    break;
                }
            if(flag)puts("Baka Chino");
            else puts("Yes");
        }
        return 0;
    }
  • 相关阅读:
    贝叶斯在机器学习中的应用(一)
    使用ajax发送的请求实现页面跳转
    关于welcom-file-list 失效
    在新的线程中使用session 出现的问题
    关于innerHtml= 与 html() 区别
    request.getAttribute()和 request.getParameter()有何区别?
    __STL_VOLATILE
    声明一个返回特定类型函数指针的函数
    关于vue的基础概念
    Vue.js结合vue-router和webpack编写单页路由项目
  • 原文地址:https://www.cnblogs.com/wondering-world/p/13416576.html
Copyright © 2011-2022 走看看