[POJ3417]Network/闇の連鎖

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

Input
第一行包含两个整数 N 和 M。
之后 N – 1 行,每行包括两个整数 A 和 B,表示 A 和 B 之间有一条主要边。
之后 M 行以同样的格式给出附加边。
N≤100 000,M≤200 000。数据保证答案不超过 2^31 – 1

Output
输出一个整数表示答案

Sample Input
4 1
1 2
2 3
1 4
3 4

Sample Output
3


这题直接上树链剖分,考虑每条附加边会在哪些主要变切断后还能联系两个联通块,如果一条主要边切断后,两个联通块之间有两条以上的边连接,那么对答案的贡献就是0;如果有一条边连接,那么对答案的贡献就是1;如果没有边连接,那么对答案的贡献就是m

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)  print(x/10);
    putchar(x%10+'0');
}
const int N=1e5,M=2e5;
int v[N+10];
struct S1{
    int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],tot;
    int top[N+10],Rem[N+10],size[N+10],deep[N+10],fa[N+10];
    void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
    void insert(int x,int y){join(x,y),join(y,x);}
    void dfs(int x){
        deep[x]=deep[fa[x]]+1,size[x]=1;
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa[x]) continue;
            fa[son]=x,dfs(son),size[x]+=size[son];
            if (size[Rem[x]]<size[son]) Rem[x]=son;
        }
    }
    void build(int x){
        if (!x) return;
        top[x]=Rem[fa[x]]==x?top[fa[x]]:x;
        build(Rem[x]);
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa[x]||son==Rem[x])    continue;
            build(son);
        }
    }
    void work(int x,int y){
        v[x]++,v[y]++;
        while (top[x]!=top[y]){
            if (deep[top[x]]<deep[top[y]])  swap(x,y);
            x=fa[top[x]];
        }
        v[deep[x]<deep[y]?x:y]-=2;
        return;
    }
    void get_v(int x){
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa[x]) continue;
            get_v(son);
            v[x]+=v[son];
        }
    }
}HLD;//Heavy-Light Decomposition 
int main(){
    int n=read(),m=read(),Ans=0;
    for (int i=1;i<n;i++){
        int x=read(),y=read();
        HLD.insert(x,y);
    }
    HLD.dfs(1),HLD.build(1);
    for (int i=1;i<=m;i++){
        int x=read(),y=read();
        HLD.work(x,y); 
    }
    HLD.get_v(1);
    for (int i=2;i<=n;i++)  Ans+=!v[i]?m:v[i]==1?1:0;
    printf("%d\n",Ans);
    return 0;
}

转载于:https://www.cnblogs.com/Wolfycz/p/9746636.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值