给一棵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;
}