POJ3417 Network【LCA+树上差分】

传送门

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.

题意:

传说中的暗之连锁被人们称为Dark。Dark是人类内心的黑暗的产物,古今中外的勇者们都试图打倒它。经过研究,你发现Dark呈现无向图的结构,图中有N个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark有N – 1条主要边,并且Dark的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark还有M条附加边。 你的任务是把Dark斩为不连通的两部分。一开始Dark的附加边都处于无敌状态,你只能选择一条主要边切断。一旦你切断了一条主要边,Dark就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断 Dark的一条附加边。现在你想要知道,一共有多少种方案可以击败Dark。注意,就算你第一步切断主要边之后就已经把Dark斩为两截,你也需要切断一条附加边才算击败了Dark。

思路:
最开始的我的想法是判桥,但是给了我们两类边,而且思路好像也不正确。我们要注意到题目上给的N - 1条主要边构成的一棵,然后再给我们 m 条次要边,肯定会与树上的边形成环。

由上面的图我们可以发现:如果一条主要边被覆盖的次数==0,那么我们任意砍断一条次要边都可以。如果一条被覆盖一次,我们只能通过砍断覆盖的那一条次要边使图分成两部分。如果一条边被覆盖2次或2次以上,则不可能通过砍掉这条主要边达成要求

如果给你一条次要边 edge(x,y),我们就需要将 x - LCA(x,y) 和 y - LCA(x,y) 上的所有边加上1,如果暴力加上1,肯定会T掉。树上的一条链我们可以看作是一个线性结构。我们可以通过将 x - root 和 y- root 上所有边 加上1 ,LCA(x,y) - root 上的所有边在减去2 。这样可以达到同样的效果。这个问题我们可以用差分来简单解决。我们用 dp[i] 表示 i 到 i 的父亲结点这条边被覆盖过几次。我们只需要将 dp[ x ] ++,dp[ y ]++ ,dp[ LCA( x , y ) ] - = 2。然后从叶子结点往根上面回溯。dp[ father[ i ] ] += dp[ i ]。

代码:
 

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int mod=1e9+7;
const int INF=1e9;

int n,m;

struct node
{
    int e;
    int p;
} load[200005],edge[200005];
int head[100005],sign;
int rehead[100005];///记录深度的邻接表

void add_edge(int s,int e)
{
    load[++sign]=node{e,head[s]};
    head[s]=sign;
}

void add(int s,int e)
{
    edge[++sign]=node{e,rehead[s]};
    rehead[s]=sign;
}

int depth[100005];
int grand[100005][20],N;
int dp[100005];
vector<int>q[100005];

void dfs(int s,int pre)
{
    for(int i=1; i<=N; i++)
        grand[s][i]=grand[grand[s][i-1]][i-1];
    for(int i=head[s]; i!=-1; i=load[i].p)
    {
        int e=load[i].e;
        if(e!=pre)
        {
            depth[e]=depth[s]+1;
            grand[e][0]=s;
            add(depth[e],e);    ///不能用vector,会T
            dfs(e,s);
        }
    }
}

int get_lca(int a,int b)
{
    if(depth[a]>depth[b])
        swap(a,b);
    for(int i=N; i>=0; i--)
        if(depth[b]>=depth[a]&&depth[grand[b][i]]>=depth[a])
            b=grand[b][i];
    for(int i=N; i>=0; i--)
    {
        if(grand[a][i]!=grand[b][i])
        {
            a=grand[a][i];
            b=grand[b][i];
        }
    }
    return a==b?a:grand[b][0];
}

void init()
{
    N=log2(n);
    sign=0;
    memset(head,-1,sizeof(head));
    memset(rehead,-1,sizeof(rehead));
    memset(depth,0,sizeof(depth));
    memset(dp,0,sizeof(dp));
    memset(grand,0,sizeof(grand));
    depth[0]=-1;
}

int main()
{
    int s,e;
    while(~scanf("%d %d",&n,&m))
    {
        init();
        for(int i=1; i<n; i++)
        {
            scanf("%d %d",&s,&e);
            add_edge(s,e);
            add_edge(e,s);
        }
        sign=0;
        dfs(1,-1);
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&s,&e);
            int x=get_lca(s,e);
            dp[s]++;
            dp[e]++;
            dp[x]-=2;
        }
        int sum=0;
        for(int i=n; i>=1; i--)
        {
            for(int j=rehead[i]; j!=-1; j=edge[j].p)
            {
                e=edge[j].e;
                if(dp[e]==0)
                    sum+=m;
                if(dp[e]==1)
                    sum++;
                dp[grand[e][0]]+=dp[e];///向上更新
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值