闇の連鎖

【题目】

传说中的暗之连锁被人们称为 Dark。Dark 是人类内心的 黑暗的产物,古今中外的勇者们都试图打倒它。经过研究, 你发现 Dark 呈现无向图的结构,图中有 N 个节点和两类边, 一类边被称为主要边,而另一类被称为附加边。Dark 有 N – 1 条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark 还有 M 条附加边。 你的任务是把 Dark 斩为不连通的两部分。一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。一 旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切 断。但是你的能力只能再切断 Dark 的一条附加边。现在你想要知道,一共有多少种方案可 以击败 Dark。注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断 一条附加边才算击败了 Dark。

【题意】

有N-1(节点数-1)条主要边,使节点相通。再加上M条附加边。我们只能删除一条主要边后,才能删除附加边。而我们需要算出,必须先删除一条主要边并且也必须删除一条附加边后,能把图变成不连通的两截的方法有多少种。

【题解】

本来拥有的N-1(节点数-1)条主要边,让图变成了一颗树。在这种情况下,只要删除一条主要边就能让树变成不连通的两截。而加上附加边后,条件就要变了。因为附加边的关系,使连接的两节点x,y之间的路径都多了一条可连接的边,我们可以说,两点之间的路径上的边,被新添加的边覆盖了(如图,黑色为被覆盖0次的主要边,蓝色为被覆盖1次的主要边,粉色为被覆盖两次的主要边,棕色为附加边)。

图片说明

而这时,要让图变成两截只有两种方法:

  1. 选择覆盖边数位0的边,如上图中唯一的黑边。进行删除,然后随便删除一条附加边。那么就达到了胜利的条件。
  2. 选择覆盖边数位1的边,如上图中的蓝边。进行删除,然后删除让它被覆盖的那条附加边。

除此以外,不管是直接删除一条主要边就能把图变成两截,却没有附加边可以删除。或者删除一条主要边或者附加边,不能使图变成两截。都是失败的。
所以要找到总的胜利数的做法,就是利用树的最近公共祖先和树上差分进行解题。找到附加边连接的x,y的最近公共祖先,最近公共祖先的标记值-2,而x,y分别加一。以此表明,这条路径上的边,被覆盖了。而查找这条路径的被覆盖次数,只需要dfs遍历一遍树,对自己的值跟子树的值的和相加。如果最终统计为0,就表示跟父亲的连接的边没有被覆盖过,可以直接删除这条边和任意的一条附加边m(如果m等于0,那么加的也是0,这样可以顺便排除掉没有附加边可以删除的情况)。如果统计为1,就表示跟父亲连接的边被覆盖过一次,只能删除当前边和与其关联的一条边,所以为1中情况,总数加1。而最终树的dfs遍历完成后,期间加的总数,就是我们需要的结果。

时间复杂度:O(N+M)

#include<iostream>
#include<cstring>
#include<sstream>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<list>
#include<set>
#include<map>
#include<algorithm>
#define fi first
#define se second
#define MP make_pair
#define P pair<int,int>
#define PLL pair<ll,ll>
#define lc (p<<1)
#define rc (p<<1|1)    
#define MID (tree[p].l+tree[p].r)>>1
#define Sca(x) scanf("%d",&x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x)
#define Scl2(x,y) scanf("%lld%lld",&x,&y)
#define Scl3(x,y,z) scanf("%lld%lld%lld",&x,&y,&z)
#define Pri(x) printf("%d\n",x)
#define Prl(x) printf("%lld\n",x)
#define For(i,x,y) for(int i=x;i<=y;i++)
#define _For(i,x,y) for(int i=x;i>=y;i--)
#define FAST_IO std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define STOP system("pause")
#define ll long long
const int INF=0x3f3f3f3f;
const ll INFL=0x3f3f3f3f3f3f3f3f;
const double Pi = acos(-1.0);
using namespace std;
template <class T>void tomax(T&a,T b){ a=max(a,b); }  
template <class T>void tomin(T&a,T b){ a=min(a,b); }
const int N=1e5+5;
const int M=2e5+5;
int dis[N],f[N][20],d[N],head[N];
int idx=0,t=0,ans=0;
int n=0,m=0; 
struct E{
    int v;
    int nxt;
}edge[M<<1];
void bfs(){
    queue<int> q; q.push(1);  
    f[1][0]=1; d[1]=1;
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];~i;i=edge[i].nxt){
            int v=edge[i].v;
            if(d[v]) continue;
            d[v]=d[u]+1;
            f[v][0]=u;
            for(int j=1;j<=t;j++)
                f[v][j]=f[f[v][j-1]][j-1];
            q.push(v);
        }
    }
}
int LCA(int u,int v){
    if(d[u] > d[v]) swap(u,v);
    _For(i,t,0)
        if(d[f[v][i]] >= d[u])
            v=f[v][i];
    if(u==v) return u;
    _For(i,t,0){
        if(f[u][i]!=f[v][i]){
            u=f[u][i];
            v=f[v][i];
        }
    }
    return f[u][0];
}
void get_ans(int u){
    for(int i=head[u];~i;i=edge[i].nxt){
        int v=edge[i].v;
        if(v==f[u][0]) continue;
        get_ans(v);
        dis[u]+=dis[v];
    }
    if(u!=1 && dis[u]==0) ans+=m; 
    else if(u!=1 && dis[u]==1) ans++;
}

void add_edge(int u,int v){ edge[idx]=E{v,head[u]}; head[u]=idx++; } 

void init(int n){
    idx=0;
    t=(int)(log(n)/log(2)) + 1; 
    For(i,1,n){
        head[i]=-1;
    }
}
int main(){
    Sca2(n,m);
    init(n);
    For(i,1,n-1){
        int u,v;
        Sca2(u,v);
        add_edge(u,v); add_edge(v,u); 
    }
    bfs();
    For(i,1,m){
        int u,v; Sca2(u,v);
        dis[u]++; dis[v]++; dis[LCA(u,v)]-=2;
    }
    get_ans(1);
    printf("%d\n",ans);
}