zoukankan      html  css  js  c++  java
  • 树上差分:闇の連鎖 题解

    题目描述

    传说中的暗之连锁被人们称为 Dark。

    Dark 是人类内心的黑暗的产物,古今中外的勇者们都试图打倒它。

    经过研究,你发现 Dark 呈现无向图的结构,图中有 N 个节点和两类边,一类边被称为主要边,而另一类被称为附加边。

    Dark 有 N – 1 条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。

    另外,Dark 还有 M 条附加边。

    你的任务是把 Dark 斩为不连通的两部分。

    一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。

    一旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。

    但是你的能力只能再切断 Dark 的一条附加边。

    现在你想要知道,一共有多少种方案可以击败 Dark。

    注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断一条附加边才算击败了 Dark。
    输入格式

    第一行包含两个整数 N 和 M。

    之后 N – 1 行,每行包括两个整数 A 和 B,表示 A 和 B 之间有一条主要边。

    之后 M 行以同样的格式给出附加边。

    输出格式
    输出一个整数表示答案。

    数据范围
    N≤100000,M≤200000,数据保证答案不超过231−1

    样例
    输入样例:
    4 1
    1 2
    2 3
    1 4
    3 4
    输出样例:
    3

    在没有附加边的情况下,我们发现这是一颗树,那么再添加条附加边(x,y)后,会造成(x,y)之间产生一个环
    如果我们第一步截断了(x,y)之间的一条路,那么我们第二次只能截掉(x,y)之间的附加边,才能使其不连通;
    我们将每条附加边(x,y)称为将(x,y)之间的路径覆盖了一遍;
    因此我们只需要统计出每条主要边被覆盖了几次即可;
    对于只被覆盖一次的边,第二次我们只能切断(x,y)边,方法唯一;
    如果我们第一步切断了被覆盖0次的边,那么我们已经将其分为两部分,那么第二部只需要在m条附加边中任选一条即可,如果第一步截到被覆盖超过两次的边,将无法将其分为两部分;
    运用乘法原理,我们累加答案;
    那么怎么标记我们的边(x,y)被覆盖了几次呢,那么我们可以使用树上差分,是解决此类问题的经典套路;
    我们想,对于一条边(x,y),我们添加一条边;
    那么只会对x到lca(x,y)到y上的边产生影响,对于(x,y)我们将x节点的权值+1,y节点的权值+1,另lca(x,y)的权值-2,画图很好理解,那么我们进行一遍dfs求出每个节点权值,那么这个值就是节点父节点连边被覆盖的次数,按上述方法累加答案即可;
    时间复杂度分析:O(N+M)

    #include<bits/stdc++.h>
    using namespace std;
    #define N 500100
    
    int x,y,n,m,tot,ans,p[N],f1[N],lin[N],f[N][25],d[N],vis[N];
    
    template<typename T>inline void read(T &x)
    {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
        while(isdigit(ch))  {x=(x<<1)+(x<<3)+(ch^48);  ch=getchar();}
        x*=f;
    }
    
    struct gg {
        int x,y,next;
    }a[N<<1];
    
    inline void add(int x,int y) {
        a[++tot].x=x;
        a[tot].y=y;
        a[tot].next=lin[x];
        lin[x]=tot;
    }
    
    void bfs(int x) {
        queue<int> q;
        q.push(1);
        d[1]=1;
        while(q.size()) {
            int x=q.front();
            q.pop();
            for(int i=lin[x];i;i=a[i].next) {
                int y=a[i].y;
                if(d[y]) continue;
                d[y]=d[x]+1;
                f[y][0]=x;
                for(int j=1;j<=23;j++){
                    f[y][j]=f[f[y][j-1]][j-1];
                }
                q.push(y);
            }
        }
    }
    
    inline int lca(int x,int y) {
        if(d[x]>d[y]) swap(x,y);
        for(int i=23;i>=0;i--) {
            if(d[f[y][i]]>=d[x]) y=f[y][i];
        }
        if(x==y) return x;
        for(int i=23;i>=0;i--) {
            if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        }
        return f[x][0];
    }
    
    inline void dfs(int x) {
        vis[x]=1;
        for(int i=lin[x];i;i=a[i].next) {
            int y=a[i].y;
            if(vis[y]) continue;
            dfs(y);
            p[x]+=p[y];
        } 
    }
    
    int main() {
        read(n); read(m);
        int x,y;
        for(int i=1;i<n;i++) {
            read(x);read(y);
            add(x,y);
            add(y,x);
        }
        bfs(1);
        for(int i=1;i<=m;i++) {
            read(x);read(y);
            p[x]++,p[y]++;
            p[lca(x,y)]-=2;
        }
        dfs(1);
        for(int i=2;i<=n;i++) {
            if(!p[i]) ans+=m;
            if(p[i]==1) ans+=1;
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code
  • 相关阅读:
    面试问烂的 MySQL 四种隔离级别,看完吊打面试官!
    一周 GitHub 开源项目推荐:阿里、腾讯、陌陌、bilibili……
    干货收藏 | Java 程序员必备的一些流程图
    IntelliJ IDEA 快捷键终极大全,速度收藏!
    我的天!史上最烂的项目:苦撑 12 年,600 多万行代码...
    模板中如何添加不定个数的常数
    SFINAE简单实例
    Sequentially-consistent ordering
    hierarchical_mutex函数问题(C++ Concurrent in Action)
    不同AI学科之间的联系
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/11030221.html
Copyright © 2011-2022 走看看