牛客IOI周赛17-提高组( A - 小王子 树上边差分)

题目链接

A-小王子

题意:对于题意可以分解为.

有n个点,由n-1条白色的边连接,同时又有着m条边.

因为这里说到了白色的边都不重复也不缠绕,显然是n-1条边构成树边.

然后有m条非树边。然后问我们删去一条树边和一条非树边使树分为两部分。这条边完全断开.

思路:参考来自:博客

我们可以从每条树边出发。

对于每条树边。

如果没有非树边覆盖到它.那么删去所有中任意的非树边都可以.即m种方案.

如果有一条非树边覆盖到它,那么只有删去这条树边才可以.即1种方案.

如果有两条及以上的非树边覆盖到它,那么删去非树边中的任意一条都不行,依旧会连通.所以此时方案为0.

统计树边的覆盖情况采用树上差分的边差分.

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
vector<int>G[N];
int fa[N][25],dep[N],sz[N],n,m,u,v;
void dfs(int u,int f)
{
    fa[u][0]=f;
    dep[u]=dep[f]+1;
    for(int v:G[u]) if(v!=f) dfs(v,u);
}
void init()
{
    dfs(1,1);
    for(int k=1;k<=24;++k)
    for(int i=1;i<=n;++i)
    fa[i][k]=fa[fa[i][k-1]][k-1];
}
int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    for(int k=24;~k;--k){
        if(dep[fa[u][k]]>=dep[v]) u=fa[u][k];
    }
    if(u==v) return u;
    for(int k=24;~k;--k){
        if(fa[u][k]!=fa[v][k]) u=fa[u][k],v=fa[v][k];
    }
    return fa[u][0];
}

long long ans;
void dfs2(int u,int f)
{
    for(int v:G[u]){
        if(v==f) continue;
        dfs2(v,u);
        sz[u]+=sz[v];
        if(sz[v]==0) ans+=m;
        else if(sz[v]==1) ans++;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=2;i<=n;++i){
        scanf("%d%d",&u,&v);
        G[u].push_back(v);G[v].push_back(u);
    }
    init();
    //printf("%d\n",lca(4,5));
    for(int i=1;i<=m;++i){
        scanf("%d%d",&u,&v);
        sz[u]++;sz[v]++;
        sz[lca(u,v)]-=2;
    }
    dfs2(1,1);
    printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/106737122