tarjan -LCA POJ-3417-Network

来源:互联网 发布:ps4全境封锁网络卡 编辑:程序博客网 时间:2024/04/26 06:16


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


0 0
原创粉丝点击