poj3417 Network LCA+DP

     给一棵N个节点的树,添加M条边构成一个新的图。现在可以删除一条新边,删除一条老边,问有多少种删除方式,可以使得删除新老两条边后的图不再联通..这题思路也挺神奇的,果然是脑子太死板了不利于做dp的题么= =.....每增加一条新边,一定可以构成一个环,那么我们统计一下树上每条边分别被多少个环覆盖。如果某条边没有被环覆盖,那么它是一个桥,删除他之后,M条新边任意删一条都符合要求,所以ans+=m,如果某条边恰好被一个环覆盖,那么删除它,再删除这个换上的新边也符合要求,因为老边构成的是一棵树,所以在这个环上新边一定只有一条,所以ans++。那么现在问题就是如何统计了,这里可以用树形DP去做,dp[u]表示节点u的父边被多少个环覆盖,那么每新增一条新边(x,y),有dp[x]++,dp[y]++,同时dp[lca(x,y)]-=2.之后dfs一边,dp[u]+=dp[v]就可以了,最后扫一遍dp数组就能得到答案...

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=101000;
int n,m,p,q;
struct EDGE
{
    int v,w,next;
}edge[maxn<<2];
int g1[maxn],g2[maxn];
int fa[maxn];
int res[maxn][3];
bool vis[maxn];
int dp[maxn];
int find(int x)
{
    if (x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}

void lca(int u)
{
    fa[u]=u;
    vis[u]=true;
    int v;
    for (int j=g2[u]; j!=-1; j=edge[j].next)
    {
        v=edge[j].v;
        if (vis[v])
        {
            res[edge[j].w][2]=find(v);
        }
    }
    for (int j=g1[u]; j!=-1; j=edge[j].next)
    {
        v=edge[j].v;
        if (!vis[v])
        {
            lca(v);
            fa[v]=u;
        }
    }
}
void dfs(int u,int fa)
{
    int v;
    for (int j=g1[u]; j!=-1; j=edge[j].next)
    {
        v=edge[j].v;
        if (v!=fa)
        {
            dfs(v,u);
            dp[u]+=dp[v];
        }
    }
}
void init()
{
    memset(g1,-1,sizeof g1);
    memset(g2,-1,sizeof g2);
    p=0;
    memset(vis,false,sizeof vis);
    memset(dp,0,sizeof dp);
}
int main()
{
//    freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        init();
        int x,y;
        for (int i=1; i<n; i++)
        {
            scanf("%d%d",&x,&y);
            edge[p].v=y;
            edge[p].next=g1[x];
            g1[x]=p;
            p++;
            edge[p].v=x;
            edge[p].next=g1[y];
            g1[y]=p;
            p++;
        }
        for (int i=1; i<=m; i++)
        {
            scanf("%d%d",&x,&y);
            edge[p].v=y;
            edge[p].w=i;
            edge[p].next=g2[x];
            g2[x]=p;
            p++;
            edge[p].v=x;
            edge[p].w=i;
            edge[p].next=g2[y];
            g2[y]=p;
            p++;

            res[i][0]=x;
            res[i][1]=y;
        }
        lca(1);
        for (int i=1; i<=m; i++)
        {
            dp[res[i][0]]++;
            dp[res[i][1]]++;
            dp[res[i][2]]-=2;
        }
        dfs(1,-1);
        ll ans=0;
        for (int i=2; i<=n; i++)
        if (dp[i]==0) ans+=(ll)m;
        else if (dp[i]==1) ans+=1LL;
        cout<<ans<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值