AcWing 397. 逃不掉的路(边双连通分量缩点成树 + 树链剖分乱搞)

整理的算法模板合集: ACM模板


在这里插入图片描述
我们知道在同一个边双连通分量中的点没有必经边(因为至少有两条分离的路径)。

所以我们直接tarjan求出桥后缩点,然后求一下树上两点间的距离即可。

那么如何求树上两点间距离呢,当然是用lca+bfs处理啦,答案就是dep[u]+dep[v]-2*dep[LCA(u,v)];

等等,在这里插入图片描述

当然是树链剖分乱搞啦!

树链剖分乱搞:
在这里插入图片描述
正解lca:
在这里插入图片描述
啊这,树链剖分天下第一!(其实用树链剖分还TLE了一发,又交了一次运气好过了…)

其实主要是树链剖分给我们提供了一种暴力的思路,让我们不用分析想正解就能有机会AC,比较适合我这种没脑子的人hhh

树链剖分AC代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 2000007, M = 5000007, INF = 0x3f3f3f3f;

int n, m;
int head[N], ver[M], nex[M], tot;
int hc[N], vc[M], nc[M], tc;
int dcc[N], dcc_cnt;
int bridge[N], dfn[N], low[N], num;
int root = 1;

void add(int x, int y)
{
    ver[tot] = y;
    nex[tot] = head[x];
    head[x] = tot ++ ;
}

void add_c(int x, int y)
{
    vc[tc] = y;
    nc[tc] = hc[x];
    hc[x] = tc ++ ;
}

void tarjan(int x, int in_edge = 0)
{
    dfn[x] = low[x] = ++ num;
    for(int i = head[x]; ~i; i = nex[i]){
        if(i == (in_edge ^ 1))continue;
        int y = ver[i];
        if(!dfn[y]){
            tarjan(y, i);
            low[x] = min(low[x], low[y]);
            if(dfn[x] < low[y]){
                bridge[i] = bridge[i ^ 1] = true;
            }
        }
        else low[x] = min(low[x], dfn[y]);
    }
}

void dfs(int x, int in_edge = 0)
{
    dcc[x] = dcc_cnt;
    for(int i = head[x]; ~i; i = nex[i]){
        int y = ver[i];
        if(i == (in_edge ^ 1) || bridge[i])continue;
        if(dcc[y] == 0)dfs(y, i);
    }
}

int a[N], a_after[N];
struct Tree{
    int l, r;
    int lz;
    int sum;
}tr[N * 4];
int son[N];//重儿子
int id[N], fa[N], cnt, deep[N], sizes[N];
int top[N]; //重链顶点
int res = 0;

inline void pushup(int p)
{
    tr[p].sum = tr[p << 1].sum + tr[p << 1 | 1].sum;
}

inline void pushdown(int p)
{
    auto &root = tr[p], &left = tr[p << 1], &right = tr[p << 1 | 1];
    if(!root.lz)return ;
    left.lz += root.lz;
    right.lz += root.lz;
    left.sum = left.sum + root.lz * (left.r - left.l + 1);
    right.sum = right.sum + root.lz * (right.r - right.l + 1);
    root.lz = 0;
}

void build(int p, int l, int r)
{
    tr[p] = {l, r, 0, 0};
    if(l == r){
        tr[p].sum = 0;
        return ;
    }
    int mid = l + r >> 1;
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);
    pushup(p);
}

int query(int p, int l, int r)
{
    if(tr[p].l >= l && tr[p].r <= r){
       return tr[p].sum;
    }
    pushdown(p);
    int mid = tr[p].l + tr[p].r >> 1;
    int res = 0;
    if(l <= mid)res = (res + query(p << 1, l, r));
    if(r > mid)res = (res + query(p << 1 | 1, l, r));
    return res;
}

void modify(int p, int l, int r, int k)
{
    if(tr[p].l >= l && tr[p].r <= r){
        tr[p].lz += k;
        tr[p].sum += k * (tr[p].r - tr[p].l + 1);
        return ;
    }
    int mid = tr[p].l + tr[p].r >> 1;
    pushdown(p);
    if(l <= mid)modify(p << 1, l, r, k);
    if(r > mid)modify(p << 1 | 1, l, r, k);
    pushup(p);
    return ;
}

//-----------------------线段树
//找重儿子,记录深度
void dfs_son(int x, int father, int deeps)
{
    deep[x] = deeps;
    fa[x] = father;
    sizes[x] = 1;
    int max_son = -1;
    for(int i = hc[x]; ~i; i = nc[i]){
        int y = vc[i];
        if(y == father)continue;
        dfs_son(y, x, deeps + 1);
        sizes[x] += sizes[y];
        if(sizes[y] > max_son)son[x] = y,max_son = sizes[y];
    }
}

//链接重链
void dfs_build(int x, int topfa)
{
    id[x] = ++ cnt;
    a_after[cnt] = a[x];
    top[x] = topfa;
    if(!son[x])return ;
    dfs_build(son[x], topfa);
    for(int i = hc[x]; ~i; i = nc[i]){
        int y = vc[i];
        if(y == fa[x] || y == son[x])continue;
        dfs_build(y, y);
    }
}

int query_range(int x, int y)
{
    int res = 0;
    //类似并查集
    while(top[x] != top[y]){
        if(deep[top[x]] < deep[top[y]])
            swap(x, y);
      
        res = (res + query(1, id[top[x]], id[x]));
        x = fa[top[x]];//往上跳
    }
    if(deep[x] > deep[y])
        swap(x, y);
    res = res + query(1, id[x], id[y]);
    return res;
}

void update_range(int x, int y, int z)
{
    while(top[x] != top[y]){
        if(deep[top[x]] < deep[top[y]])
            swap(x, y);
        modify(1, id[top[x]], id[x], z);
        x = fa[top[x]];
    }
    if(deep[x] > deep[y])
        swap(x, y);
    modify(1, id[x], id[y], z);
}

int get_dist(int x, int y)
{
    update_range(x, y, 1);
    int res = query_range(x, y) - 1;
    update_range(x, y, -1);
    return res;
}

int main()
{
    memset(head, -1, sizeof head);
    memset(hc, -1, sizeof hc);
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; ++ i){
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y), add(y, x);
    }

    tarjan(1);
    //cout << "ok" << endl;
    for(int i = 1; i <= n ;++ i){
        if(dcc[i] == 0)
            ++ dcc_cnt, dfs(i);
    }
    //cout << "ok" << endl;
    for(int x = 1; x <= n; ++ x){
        for(int i = head[x]; ~i;i = nex[i]){
            int y = ver[i];
            if(dcc[x] != dcc[y])
                add_c(dcc[x], dcc[y]);
                //add_c(dcc[y], dcc[x]);
        }
    }

    dfs_son(root, 0, 1);
    //cout << "ok1" << endl;
    dfs_build(root, root);
    //cout << "ok2" << endl;
    build(1, 1, n);
    //cout << "ok3" << endl;
    int q;
    scanf("%d", &q);
    while(q -- ){
        int x, y;
        scanf("%d%d", &x, &y);
        printf("%d\n", get_dist(dcc[x], dcc[y]));
    }
    return 0;
}

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页