先放一下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;
}