【题解】一本通1553:【例 2】暗的连锁

[题目](http://ybt.ssoier.cn:8088/problem_show.php?pid=1553

前置知识:倍增求LCA(当然不倍增也行)

1553:【例 2】暗的连锁

时间限制: 1000 ms 内存限制: 524288 KB
提交数: 456 通过数: 201

【题目描述】

原题来自:POJ 3417

Dark 是一张无向图,图中有 NN 个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark 有 N–1N–1 条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark 还有 MM 条附加边。

你的任务是把 Dark 斩为不连通的两部分。一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。一旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断 Dark 的一条附加边。

现在你想要知道,一共有多少种方案可以击败 Dark。注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断一条附加边才算击败了 Dark。

【输入】

第一行包含两个整数 NN 和 MM;

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

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

【输出】

输出一个整数表示答案。

【输入样例】

4 1
1 2
2 3
1 4
3 4

【输出样例】

3

【提示】

数据范围与提示:

对于 20% 的数据,1≤N,M≤1001≤N,M≤100;

对于 100% 的数据,1≤N≤105,1≤M≤2×1051≤N≤105,1≤M≤2×105 。数据保证答案不超过 231−1231−1。
(看数据会发现:两点之间直接相连的附加边可能有很多条;自己与自己也能连接一条附加边)

题解

附加边:

添加一条附加边,不仅会造成这条边直接相连的两个点之间的主要边受阻,还会造成其他的一些边受阻
这里的受阻,是指砍除两点之间的主要边之后,必须再砍除这条附加边,才能将树分成两段

受阻范围:

就是从附加边连接的两点,到它们的LCA之间的所有边。也就是这两点之间的最短路径

贡献解的条件:

若砍除一条主要边之后,再砍掉它受阻的附加边,能将原树分成两半,那么这条边受阻的附加边必然不大于2
所以我们要记录每一条主要边受几条附加边的阻碍

怎么记录?:

只要这条边在附加边阻碍范围的最短路径上,也就是在lca为根的树上,就会受阻;但一旦这条边出了lca子树,便不受阻。可以理解为在lca子树里,lca和附加边造成的阻碍相互抵消了。
因此我们可以利用差分的思想

变量和函数

k[]&&cha(x,y):一条附加边连接x和y,它们的LCA用函数find_LCA(x,y)找到,那么将k[x]和k[y]都加一,将k[find_LCA(x,y)]减二,就实现了差分
quan[]&&get_quan():对于连接点u和它的父亲fa的边x,计算出以u为根的子树的所有节点的k[]之和,就是边x所受的阻碍次数(这个不难想诶,,,)
get_LCA&&f[][]&&dep[]:求LCA嘛,不多说

方案数

唉,交了三遍才发现题目让求的是方案数,不是主要边的数量。。。
如果这条主要边受阻0次,那砍完这条主要边后,从所有的附加边中随便砍一条,答案加m;
如果它受阻1次,那砍完它后必须再砍断阻碍它的附加边,答案加1
所以:code:

#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define F(i,a,b) for(int i=a;i<=b;i++)
#define UF(i,a,b) for(int i=a;i>=b;i--)
#define N 100010
#define M 200010
using namespace std;
int n, m, next[N], fir[N], to[N], len, k[N], quan[N], x, y, f[N][25], ans, dep[N];
void ins(int x, int y)
{
    len++;
    next[len]=fir[x];
    to[len]=y;
    fir[x]=len;
    /*len++;
    next[len]=fir[y];
    to[len]=x;
    fir[y]=len;*/
}
void deal_first(int u, int fa)
{
    dep[u]=dep[fa]+1;
    F(i,0,19){
        f[u][i+1]=f[f[u][i]][i];
    }
    for(int e=fir[u];e;e=next[e]){
        int v=to[e];
        f[v][0]=u;
//      cout << v << " ";
        deal_first(v,u);
    }//cout << endl;
}
int find_LCA(int x, int y)
{
    if(x==y){
        return x; 
    }
    if(dep[x]<dep[y])   swap(x,y);
    UF(i,20,0){
        if(dep[f[x][i]]>=dep[y])    x=f[x][i];
        if(x==y){
            return x; 
        }
    }
    UF(i,20,0){
        if(f[x][i]!=f[y][i]){
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0]; 
}
void cha(int x, int y)
{
    k[x]+=1;
    k[y]+=1;
    k[find_LCA(x,y)]-=2;
    return;
}
void get_quan(int x, int u, int fa)
{
//  cout << "*";
    if(quan[x] != N)    return;
    quan[x]=0;
    for(int e=fir[u];e;e=next[e]){
        int v=to[e];
        get_quan(e,v,u);
        quan[x]+=quan[e];
    }
    quan[x]+=k[u];
    if(quan[x]<=1 && x!=0){
        if(quan[x]==0)  ans+=m;
        if(quan[x]==1)  ans+=1;
    }
    return;
}

void check()
{
    printf("len=%d\n",len);
    F(i,1,n){
        for (int e=fir[i];e;e=next[e])
        {
            cout << to[e] << " ";
        }cout << endl;
    }
}void check02()
{
    F(i,1,n)    cout<<dep[i]<<" ";
    cout << endl;
    F(i,1,n){
        F(j,0,2)
            cout << f[i][j] << " ";
        cout << endl;
    }
}
void check03()
{
    F(i,1,n)    cout << k[i] << " ";
    cout << endl;
}
void check04()
{
    F(i,0,len)  cout << quan[i] << " ";
    cout << endl;
}
int main()
{
    scanf("%d%d",&n,&m);
    F(i,1,n-1){
        scanf("%d%d",&x,&y);
        ins(x,y);
    }
    deal_first(1,0);
    F(i,1,m){
        scanf("%d%d",&x,&y);
        cha(x,y);
    }
    F(i,0,len)  quan[i]=N;
//  check();
//  check02();
//  check03();
    get_quan(0,1,0);
//  check04();
    printf("%d",ans);
    return 0;
}
/*
7 3
1 2
1 7 
2 3
2 4
2 5
3 6
3 4
4 5
4 6
*/

猜你喜欢

转载自www.cnblogs.com/ZhengkunJia/p/12275722.html