题意:给出一棵dfs生成树,让你选一些树上的边,使得所有T-Simple Circles至少包含一条你选的边。
思路:由于给出的是dfs生成树,所以这里是没有横叉边的,直觉告诉我们选择的边应该在树上尽量“高”,对于生出树上的边(u,v),可以考虑这条边是否必须被选择,如果必须被选,那么就选择它,并把v的子树上的点都标记掉,这些点的反向边都包含(u,v)了。
一条边是否必须被选可以看下面的图,(2,3)这条边必须被选当且仅当2有连向其子节点的非树边(如果不是这样,我们总可以找出更"高"的边),并且这个点没有被标记。选择(2,3)这条边以后可以发现,3及其子树中的所有的点连向它们父节点的边都已经包含(2,3)了,因此将它们都标记掉就行了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=2000+10;
const int maxm=20000+10;
struct Edge
{
int v,next;
};
Edge edges[maxn<<1],ee[maxm<<1];
int head[maxn],h[maxn],nEdge,ne;
int S[maxn],pre[maxn],c,ans,dfs_clock;
bool flag[maxn];
void AddEdge(int u,int v,int wh)
{
if(wh)
{
nEdge++;
edges[nEdge].v=v;edges[nEdge].next=head[u];
head[u]=nEdge;
}
else
{
ne++;
ee[ne].v=v;ee[ne].next=h[u];
h[u]=ne;
}
}
void dfs(int u,int fa)
{
pre[u]=++dfs_clock;
for(int k=head[u];k!=-1;k=edges[k].next)
{
int v=edges[k].v;
if(v==fa) continue;
dfs(v,u);
for(int p=h[u];p!=-1;p=ee[p].next)
{
int to=ee[p].v;
if(!pre[to]) continue;
if(!flag[to]&&pre[to]>=pre[v])
{
ans++;
int tmp=0;
for(int i=0;i<c;++i)
{
if(pre[S[i]]>=pre[v])
flag[S[i]]=true;
else S[tmp++]=S[i];
}
c=tmp;
}
}
}
S[c++]=u;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n,m;
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0) break;
memset(flag,0,sizeof(flag));
memset(pre,0,sizeof(pre));
memset(head,0xff,sizeof(head));
memset(h,0xff,sizeof(h));
nEdge=ne=-1;
int u,v;
for(int i=1;i<n;++i)
{
scanf("%d%d",&u,&v);
AddEdge(u,v,1);
AddEdge(v,u,1);
}
for(int i=n;i<=m;++i)
{
scanf("%d%d",&u,&v);
AddEdge(u,v,0);
AddEdge(v,u,0);
}
dfs_clock=c=ans=0;
dfs(1,-1);
printf("%d\n",ans);
}
return 0;
}