poj 3417 Network 【LCA】【树中增新边后 求每条树边被环所覆盖的次数】

Network

Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 4251   Accepted: 1223

Description

Yixght is a manager of the company called SzqNetwork(SN). Now she‘s very worried because she has just received a bad news which denotes that DxtNetwork(DN), the SN‘s business rival, intents to attack the network of SN. More unfortunately, the original network
of SN is so weak that we can just treat it as a tree. Formally, there are N nodes in SN‘s network, N-1 bidirectional channels to connect the nodes, and there always exists a route from any node to another. In order to protect the network
from the attack, Yixght builds M new bidirectional channels between some of the nodes.

As the DN‘s best hacker, you can exactly destory two channels, one in the original network and the other among the M new channels. Now your higher-up wants to know how many ways you can divide the network of SN into at least two parts.

Input

The first line of the input file contains two integers: N (1 ≤ N ≤ 100 000), M (1 ≤ M ≤ 100 000) — the number of the nodes and the number of the new channels.

Following N-1 lines represent the channels in the original network of SN, each pair (a,b) denote that there is a channel between node a and node b.

Following M lines represent the new channels in the network, each pair (a,b) denote that a new channel between node a and node b is added to the network of SN.

Output

Output a single integer — the number of ways to divide the network into at least two parts.

Sample Input

4 1
1 2
2 3
1 4
3 4

Sample Output

3

题意:给你一个N个点构成的树(N-1条边),又给你M条新边。现在要求你破坏一条树边和一条新边使得树分成两个部分。问这样的方案有多少种。

分析:树是无环的,在增加新边后必定成环。

这样对于每条边,根据它被环覆盖的情况可以分为三类

一,覆盖次数为0,显然只要破坏这条边就完成任务了,但题目还要求破坏一条新边。这样的话显然有M种方案(有M条新边嘛)。

二,覆盖次数为1(说明它所在环中只增加了一条新边),这样我们仅仅破坏它一条边是不够的,还要把它所在环中的新边破坏掉。这样的话方案只有一种。

三:覆盖次数大于1(说明它所在环中不只增加了一条新边),这种情况下,在破坏这条边后还需要破坏掉环中所有的新边,这样破坏新边的条数大于1了,不合题意。因此这种情况下对这条边采取任何措施都是不行的,方案数为0。

这样问题就变成——求出所有边被环覆盖次数。

思路:用num[v]记录<u,v>被覆盖的次数,其中u在DFS树中是v的父节点。

对于每条新加入的边<u,v>,有 num[u]++,num[v]++,num[LCA(u,v)] -= 2。这样处理是为了方便自底向上计算num值。因为对于LCA(u, v)的num值,自底向上计算num值时,我们必然会加上num[u]和num[v]的值,为了除去多算的num值,LCA(u,
v)当然要减2。给个图,务必自己模拟下。

如图所示:<4,5>新加的边,LCA(4, 5) = 2.

num[4] = num[5] = 1, num[2] = -2;

DFS(2)时,遍历到4和5回溯的时候,num[2] += num[4],num[2] += num[5]。

LCA的求法还是用 LCA转RMQ 实现的。

LCA转RMQ模板看这里:看模板点我

AC代码:提醒 用vector建图会TLE。

#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>
#define MAXN 100000+10
#define MAXM 200000+10
using namespace std;
struct Edge
{
    int from, to, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int vs[MAXN<<1], depth[MAXN<<1];
int id[MAXN];
int dfs_clock;
int N, M;
//vector<int> G[MAXN];
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v)
{
    Edge E = {u, v, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
void getMap()
{
    int a, b;
    for(int i = 1; i < N; i++)
        scanf("%d%d", &a, &b), addEdge(a, b), addEdge(b, a);
        //G[a].push_back(b);
        //G[b].push_back(a);
}
int dp[MAXN<<1][30];
void RMQ_init(int NN)
{
    for(int i = 1; i <= NN; i++)
        dp[i][0] = i;
    for(int j = 1; (1<<j) <= NN; j++)
    {
        for(int i = 1; i + (1<<j) - 1 <= NN; i++)
        {
            int a = dp[i][j-1];
            int b = dp[i+(1<<(j-1))][j-1];
            if(depth[a] < depth[b])
                dp[i][j] = a;
            else
                dp[i][j] = b;
        }
    }
}
int query(int L, int R)
{
    int k = 0;
    while((1<<(k+1)) <= R-L+1) k++;
    int a = dp[L][k];
    int b = dp[R-(1<<k)+1][k];
    if(depth[a] < depth[b])
        return a;
    else
        return b;
}
int LCA(int u, int v)
{
    int x = id[u];
    int y = id[v];
    if(x < y)
        return vs[query(x, y)];
    else
        return vs[query(y, x)];
}
void DFS(int u, int fa, int d)
{
    id[u] = dfs_clock;
    vs[dfs_clock] = u;
    depth[dfs_clock++] = d;
    for(int i = head[u]; i != -1; i = edge[i].next)
    //for(int i = 0; i < G[u].size(); i++)
    {
        //int v = G[u][i];
        int v = edge[i].to;
        if(v == fa) continue;
        DFS(v, u, d+1);
        vs[dfs_clock] = u;
        depth[dfs_clock++] = d;
    }
}
void find_depth()
{
    memset(vs, 0, sizeof(vs));
    memset(depth, 0, sizeof(depth));
    memset(id, 0, sizeof(id));
    dfs_clock = 1;
    DFS(1, -1, 0);
}
int num[MAXN];//num[v]记录<u,v>被环覆盖次数
void worknum(int u, int fa)
{
    for(int i = head[u]; i != -1; i = edge[i].next)
    //for(int i = 0; i < G[u].size(); i++)
    {
        //int v = G[u][i];
        int v = edge[i].to;
        if(v == fa) continue;
        worknum(v, u);
        num[u] += num[v];
    }
}
void solve()
{
    find_depth();
    RMQ_init(dfs_clock - 1);
    memset(num, 0, sizeof(num));
    for(int i = 1; i <= M; i++)//新边
    {
        int a, b;
        scanf("%d%d", &a, &b);
        num[a]++;
        num[b]++;
        num[LCA(a, b)] -= 2;
    }
    worknum(1, -1);//计算所有树边的num值
    int ans = 0;
    for(int i = 2; i <= N; i++)
    {
        if(num[i] == 0)//树边没有被覆盖
            ans += M;
        else if(num[i] == 1)//树边被覆盖一次
            ans += 1;
    }
    printf("%d\n", ans);
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        init();
        getMap();
        solve();
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-07-28 20:26:23

poj 3417 Network 【LCA】【树中增新边后 求每条树边被环所覆盖的次数】的相关文章

POJ - 3417 Network (LCA+树上差分)

题目传送门:POJ - 3417 Network 题目大意: 存在一棵n个结点的树,加入m条新边,现在要让这个图不连通,你可以切断两条边,要求切断一条原边,一条新边,求切割的方案数. 分析: 加入m条新边,假设加入新边(u,v),那么u-->lca(u,v)-->v-->u形成一个环,此时可以切断新边,和环上任意的一条原边就可以使图不连通. 可以发现若加入一条新边,给环上的计数1,表示该边被一个环覆盖,树上有些边会被多个环覆盖,此时可以分情况讨论. 1.若该边被覆盖次数是0,则断掉该边后

POJ - 3417 Network(LCA + DP)

题目大意:给出一棵N个结点的无根树,现在要在上面加上M条边,问,有多少种破坏方式(破坏一条树边,一条新边),能使这张新图变成至少两部分 解题思路:首先,假设添加的边为(u,v),那么u – > lca(u,v) –> v – >u就形成了一个环了,也就是说,每条添加的边都会在树上形成一个环 本来树上的每条边都是一条桥的,由于加了新的边了,形成了连通分量了,使得边的性质发生了些变化 首先,树边在0个连通分量里面的,那表示该树边还是桥,破坏了它,图肯定能分成两个部分,所以破坏方式就有M种了

Network POJ - 3417(LCA+dfs)

Yixght is a manager of the company called SzqNetwork(SN). Now she's very worried because she has just received a bad news which denotes that DxtNetwork(DN), the SN's business rival, intents to attack the network of SN. More unfortunately, the origina

poj 3417 Network 题解

题意: 先给出一棵树,然后再给出m条边,把这m条边连上,然后剪掉两条边,一条是原边,一条是新边,问有多少种方案能使图不连通. 思路: 从原边的角度看 1.树加边,一定成环,加一条(u,v)边就有u->lca->v上的边被覆盖一次 2.当一条边没被覆盖时,删去该边与任意一条新边都能使图不连通,即有m种方案 3.当一条边被覆盖1次时,删去与该边成环的新边,即有1种方案 4.当一条边被覆盖1次以上时,没有方案 用树形dp,dp[i]表示第i号点与其父亲相连的边被覆盖的次数.一条新边(u,v)加入则+

POJ 1236 Network of Schools (强连通分量缩点求度数)

题意: 求一个有向图中: (1)要选几个点才能把的点走遍 (2)要添加多少条边使得整个图强联通 分析: 对于问题1, 我们只要求出缩点后的图有多少个入度为0的scc就好, 因为有入度的scc可以从其他地方到达. 对于问题2, 每个入度为0的scc, 都可以补一条边可以变成强连通图, 每个出度为0的scc, 也可以补一条边使其变成强连通图. 所以答案就是max(入度为0scc个数,出度为0scc个数). #include<cstdio> #include<iostream> #inc

poj 3417 Network (LCA,路径上有值)

题意: N个点,构成一棵树.给出这棵树的结构. M条边,(a1,b1)...(am,bm),代表给树的这些点对连上边.这样就形成了有很多环的一个新”树“. 现在要求你在原树中断一条边,在M条边中断一条边,使得新”树“被分成两个部分. 问有多少种方案. 思路: 连上某条新边(a,b),则必定形成一个环.环的路径是a->...->lca(a,b)->...->b,给这些边的值都加1 . 分情况: 在原树中,若一条边的值>=2,去掉这条边,再去掉M条边中的一条,不影响原树的连通性.

POJ 3417 Network

每条额外的边加入到图中,会导致树上一条路径成环,假设没有其余边,那么要将新图分成两部分,如果想删一条成环路径上的边,那么必须把这条额外边也删除. 因此每条额外边加入时,只需将环上的边+1.最后看看每条边被加了几次,被加了x次,也就是说删除这条边,至少还要删除x条边才能被分成两半,如果一次都没有被加,意味着这条边删了就分成两半了,对答案的贡献为m:如果被加了一次,那么对答案的贡献为1:如果被加的次数超过1,那么对答案没有贡献. #include<cstdio> #include<cstri

Poj 3694 Network (连通图缩点+LCA+并查集)

题目链接: Poj 3694 Network 题目描述: 给出一个无向连通图,加入一系列边指定的后,问还剩下多少个桥? 解题思路: 先求出图的双连通分支,然后缩点重新建图,加入一个指定的边后,求出这条边两个端点根节点的LCA,统计其中的桥,然后把这个环中的节点加到一个集合中,根节点标记为LCA. 题目不难,坑在了数组初始化和大小 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #inclu

Network POJ - 3694(lca并查集+连通图求桥)

就是求出原先图中的桥的数量,在每一次询问时加入一条新边,求加入当前边后图中剩余的桥的数量 求出原先图中的桥的数量,然后减去新加入边的两端点之间的桥的数量,就是剩余桥的数量.. 用并查集把属于同一集合的放到一起(即两个点之间没有桥的) #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <queue> #include <algo