ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AcWing 352/loj 10131. 「一本通 4.4 例 2」暗的连锁

2021-08-04 23:00:37  阅读:150  来源: 互联网

标签:4.4 这个 10131 loj sum 附加 int -- include


Description

给定一棵 \(n\) 个点的树,还有 \(m\) 条非树边,问有多少种方法使得仅砍去一条树边和一条非树边使得这个图分成不相连的两(或更多)部分。

每次如果先砍去主要边后已经砍成两半,则仍要再砍一条附加边。

Solution

一看这数据范围,暴力组合肯定不可行。

思考附加边的作用:在这棵仅有主要边的数上,增加了一条附加边就会多一个带有根节点的环。

具体看一个例子:

如图,在增加边 \((4,5)\) 后,出现了 \(1-2-4-5\) 的蓝颜色环,如果在加一条边 \((3,6)\),则还会出现 \(1-2-3-5-6\) 这一个绿颜色环。

在一个环上,要砍成两部分,就需要把这个环给打断。先切断一条主要边,再把形成这个环的那个附加边去除。因此把环一分为二需要断2条边才能断开。

设 \(sum[i]\) 表示以 \(1\) 为根,\(i\) 号点与它的父亲节点连接的主要边砍掉后,还要砍掉多少条附加边才能把这个图一分为二。

即每新增一条附加边,由于环上的点需要把这条附加边砍去才能断开这个环,所以这个环的每一条主要边的 \(sum\) 值均需要 \(+1\)。

具体长这个样子:

最后要把sum的值分三类:
  • \(sum[i] = 0\),表示这个点没被任意一个环覆盖过,说明删掉这条边就已经变成两部分了,这个点有 $m $ 种方案。

  • \(sum[i] = 1\),表示这个点被一个环覆盖过,只能删除这个边后再删那个组成环的附加边,这个点有$ 1$ 种方案。

  • \(sum[i] ≥ 2\),表示这个点被多个环覆盖过,则不管怎样删也不能把图一分为二,这个点没有方案贡献。

现在就是考虑如何维护这个 \(sum\) 数组。

不难想到用数上差分+前缀和来写。

这里的前缀和采取的是第二种,即从以这个点为根的子树的和。

根据数上差分知识,可以把该条附加边连接的两点 \(x\),\(y\) 的 \(sum\) 均 \(+1\),把 \(LCA(x, y)\) 的值 \(-2\)。

然后用DFS跑一边数上前缀和就行了。

Code

// by pjx Jul.
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <stack>
#define REP(i, x, y) for(register int i = x; i < y; i++)
#define rep(i, x, y) for(register int i = x; i <= y; i++)
#define PER(i, x, y) for(register int i = x; i > y; i--)
#define per(i, x, y) for(register int i = x; i >= y; i--)
#define lc (k << 1)
#define rc (k << 1 | 1)
using namespace std;
#define int long long
const int N = 1E5 + 5;
const int M = 2 * N;
struct node{
    int u, v, next;
}ed[M];
int head[M], dep[N], f[N][21], sum[N];
int n, m, cnt;
bool b[N];
void add(int u, int v)
{
    cnt++;
    ed[cnt].u = u;
    ed[cnt].v = v;
    ed[cnt].next = head[u];
    head[u] = cnt;
}
void dfs(int x)//第一遍DFS预处理出LCA要用的f数组
{
    for(int i = 1; i <= 20; i++)
    {
        f[x][i] = f[f[x][i - 1]][i - 1];
    }
    for(int i = head[x]; i != 0; i = ed[i].next)
    {
        int v = ed[i].v;
        if(!b[v])
        {
            b[v] = 1;
            dep[v] = dep[x] + 1;
            f[v][0] = x;
            dfs(v);
        }
    }
}
void dfs2(int x)//第二遍DFS就是求前缀和
{
    for(int i = head[x]; i != 0; i = ed[i].next)
    {
        int v = ed[i].v;
        if(!b[v])
        {
            b[v] = 1;
            dfs2(v);
            sum[x] += sum[v];
        }
    }
}
int lca(int x, int y)
{
    if(dep[x] < dep[y])
    {
        swap(x, y);
    }
    int temp = dep[x] - dep[y];
    for(int i = 19; i >= 0; i--)
    {
        if(temp & (1 << i))
        {
            x = f[x][i];
        }
    }
    if(x == y)
    {
        return x;
    }
    for(int i = 19; i >= 0; i--)
    {
        if(f[x][i] != f[y][i])
        {
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}
signed main()
{
    cin >> n >> m;
    rep(i, 1, n - 1)
    {
        int x, y;
        cin >> x >> y;
        add(x, y);
        add(y, x);
    }
    b[1] = 1;
    dep[1] = 1;
    dfs(1);
    rep(i, 1, m)
    {
        int x, y;
        cin >> x >> y;
        sum[x]++;//树上差分
        sum[y]++;
        //cout << lca(x, y) << endl;
        sum[lca(x, y)] -= 2;
    }
    memset(b, 0, sizeof b);
    b[1] = 1;
    dfs2(1);
    int ans = 0;
    for(int i = 2; i <= n; i++)
    {
        if(sum[i] == 0)//分类讨论三种sum值情况
        {
            ans += m;
        }
        else if(sum[i] == 1)
        {
            ans++;
        }
    }
    cout << ans;
    return 0;
}

标签:4.4,这个,10131,loj,sum,附加,int,--,include
来源: https://www.cnblogs.com/pjxpjx/p/15100297.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有