其他分享
首页 > 其他分享> > 暗的连锁

暗的连锁

作者:互联网

https://loj.ac/problem/10131

题目描述

  给出一张图,有\(n\)个节点和两类边,一类为主要边,一类为次要边,主要边构成图的一棵生成树,求有多少种方案可以在断掉一条主要边和一条次要边后与不再连通。

思路

  我们把图看做一棵树,加上一些非树边。我们考虑如果存在一条\(x、y\)的非树边,那么树上\(x、y\)路径上的点一定要斩断这条非树边。所以对于每条非树边\((x,y)\),我们记为把树上\(x、y\)的路径覆盖了一次,由此我们可以知道,对于覆盖了\(0\)次的边,我们斩断它后选任意一条非树边都可以;对于覆盖了\(1\)次的点,我们只有一种方案;对于覆盖了\(2\)次及以上的点,我们无法实现不连通。

  而覆盖了几次这个内容我们可以用树上差分实现,由于边不好记录,我们先转化为点,对于\((x,y)\),使用树上差分,我们把\(val[x]++,val[y]++,val[lca]-2\)实现,最后每个点的实际值就是其子树的权值和。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;

int nxt[N<<1],to[N<<1],from[N<<1],tot,head[N];
void add_edge(int x,int y)
{
    nxt[++tot]=head[x];
    head[x]=tot;
    to[tot]=y;from[tot]=x;
}

int f[N][22],dep[N];
void deal_first(int u,int fa)
{
    dep[u]=dep[fa]+1;
    for(int i=0;i<20;i++)
        f[u][i+1]=f[f[u][i]][i];
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==fa)continue ;
        f[v][0]=u;
        deal_first(v,u);
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--)
    {
        if(dep[f[x][i]]>=dep[y])x=f[x][i];
        if(x==y)return y;
    }
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    return f[x][0];
}
int sum[N],val[N];
void dfs(int u,int fa)
{
    sum[u]=val[u];
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==fa)continue ;
        dfs(v,u);
        sum[u]+=sum[v];
    }
}

int read()
{
    int res=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
    return res*w;
}
void write(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9)write(x/10);
    putchar(x%10+'0');
} 
void writeln(int x)
{
    write(x);
    putchar('\n');
}

int main() 
{
    int n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        add_edge(x,y);add_edge(y,x);
    }
    deal_first(1,0);
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        int lca=LCA(x,y);
        val[x]++;val[y]++;
        val[lca]-=2;
    }
    dfs(1,0);
    int ans=0;
    for(int i=2;i<=n;i++)
        if(sum[i]==1)ans++;
        else if(sum[i]==0)ans+=m;
    writeln(ans);
    return 0;
}

标签:ch,val,int,sum,read,连锁,非树边
来源: https://www.cnblogs.com/fangbozhen/p/11788407.html