先放一下tarjan离线求LCA的模板
就是先dfs 然后访问新边的时候,如果碰到标记过的点,那么就说明被dfs过了
两个点找lca
void addedge2(int x,int y)
{
pra[e].to = y;
pra[e].next = head2[x];
head2[x] = e++;
}
int f[SIZE_D];
int findc(int x)
{
if (f[x] == x) return x;
return f[x] = findc(f[x]);
}
int flag[SIZE_D],dp[SIZE_D];
void tarjan(int ver,int fa)
{
f[ver] = ver;
for (int i = head[ver]; i != -1; i = pra[i].next){
int u = pra[i].to;
if (u != fa){
tarjan(u,ver);
f[u] = ver;
}
}
flag[ver] = 1;
///标记flag为何要放在两个图遍历的中间呢?能不能放后面?能不能放前面?
for (int i = head2[ver]; i != -1; i = pra[i].next){
int u = pra[i].to;
if (flag[u] != 0){
int t = findc(u);
dp[u]++;dp[ver]++;dp[t]-= 2;
}
}
}
啊呀呀……说一下这道题题意
题意:有一棵树,有n个点,现在再增加m条新边,问你去掉原来老边,去掉一条新边,使得图不连通,有几种方案数?
做法:一条边,假如存在在一个环中,那就去掉它,再去掉环中的另一条边就可以了。假如不存在在一个环中,那它就是一个桥(去掉这条边图不连通)。假如存在两个或者两个以上的环中,那么去掉这条边,无论再去掉哪条边,都不会使图不连通。
用点覆盖度表示当前点的上一条边在几个环中。
做法就是每次加进来一条新的边,那就找这两个节点的LCA ,d【v】++;d【u】++;d【lca】-=2;
然后dfs 每个点的dp值就是孩子节点(不包括孙子)的dp值之和
poj交题碰到的奇葩事:
1.老是502,504 我已经不爱poj了TAT
2.数组开小了居然返回的是TLE
3.没有把数组全部初始化只把数组部分初始化 居然返回的是RE
放代码
/*
给出原有边之后就先预处理所有边的父亲
每加一条边,加到u和v上就把u和v直到LCA的祖先节点都加1.然后祖先节点先加2再减2
从第一个到最后一个算每个节点的覆盖度
若覆盖度是0 那么就ans+= 新加的边
若覆盖度是1 那么就是删去这条边 ans+= 1
关于新加边存在x=y的情况,没判掉wa
图论三大坑自环重边点序号
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define SIZE_D 200005
#define SIZE_B 400005
using namespace std;
int N,M;
struct pp
{
int to,next;
}pra[SIZE_B];
int e,head[SIZE_D];
int head2[SIZE_D];
void init()
{
e = 0;
memset(head, -1, sizeof(head));
memset(head2, -1, sizeof(head2));
}
void addedge(int x,int y)
{
pra[e].to = y;
pra[e].next = head[x];
head[x] = e++;
}
void addedge2(int x,int y)
{
pra[e].to = y;
pra[e].next = head2[x];
head2[x] = e++;
}
int f[SIZE_D];
int findc(int x)
{
if (f[x] == x) return x;
return f[x] = findc(f[x]);
}
int flag[SIZE_D],dp[SIZE_D];
void tarjan(int ver,int fa)
{
f[ver] = ver;
for (int i = head[ver]; i != -1; i = pra[i].next){
int u = pra[i].to;
if (u != fa){
tarjan(u,ver);
f[u] = ver;
}
}
flag[ver] = 1;
///标记flag为何要放在两个图遍历的中间呢?能不能放后面?能不能放前面?
for (int i = head2[ver]; i != -1; i = pra[i].next){
int u = pra[i].to;
if (flag[u] != 0){
int t = findc(u);
dp[u]++;dp[ver]++;dp[t]-= 2;
}
}
}
void getdp(int ver,int fa)
{
for (int i = head[ver]; i != -1; i = pra[i].next){
int u = pra[i].to;
if (u != fa){
getdp(u,ver);
dp[ver]+=dp[u];
}
}
}
bool kn[SIZE_D];
int main()
{
//freopen("input.txt","r",stdin);
while (~scanf("%d %d",&N,&M)){
init();
memset(kn,0,sizeof (kn));//增加了kn数组看看是从哪个下标开始的
for (int i = 1; i < N; i++){
int tempx,tempy;
scanf("%d %d",&tempx,&tempy);
kn[tempx] = kn[tempy] = 1;
addedge(tempx,tempy);
addedge(tempy,tempx);
}
for (int i = 0; i < M; i++){
int tempx,tempy;
scanf("%d %d",&tempx,&tempy);
kn[tempx] = kn[tempy] = 1;
addedge2(tempx,tempy);
addedge2(tempy,tempx);
}
int ispran = 0;
memset(flag,0,sizeof(flag));
memset(dp,0,sizeof(dp));
int root;
for (int i = 0; i < SIZE_D-5; i++){
if (kn[i] == 1){
tarjan(i,-1);
getdp(i,-1);
root = i;
break;
}
}
int sum = 0;
int res = 0;
for (int i = 0; i < SIZE_D-5; i++){
if (kn[i] == 1 && i != root){
if (dp[i] == 1)res++;
if (dp[i] == 0) res +=M;
sum++;
if (sum == N) break;
}
}
printf("%d\n",res);
}
return 0;
}