hdu 6200 mustedge mustedge mustedge(树链剖分 + 树状数组 + 并查集)

题意:

开始给你一个n点,m边的无向联通图, 之后有q个询问,1操作是将u,v连边,2操作是询问u,v之间的关键路径(就是u到v无论走那条路都必须经过的边)

解法:

一个无向联通图找关键路径首先想到支配树,但要动态维护,所以必不可能。再想想如果两点在同一个环上的话,那不就环上的所有的边都不可能是关键路径了吗,那直接将这些边的权值消掉就好
所以我们直接建树,如果有多的边,可以先存起来,在q个询问前当操作1先用掉。
对于操作1,所加的边一定会成环,所以我们将树上属于这个环的的链权值清零
这里我们先将树树链剖分,放到树状数组上,在这点对应的dfn[x]的值+1,在dfn[x]+x子数大小这个位置-1, 这样树状数组就能回答从根节点到x的被消掉的关键路径的个数
操作2可以直接利用树状数组和dep来获得答案

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 7;

int t;
int n, m;
int u, v, op, q;
const ll MAXN = 2e5 + 7;
int c[MAXN];
int lowbit(int x){ return x & (-x);}

void add(int x, int y, int n) 
{
    //  下标为x 的数 增加 y  (相对修改)
	for (int i = x; i <= n; i += lowbit(i)) c[i] += y;
}
int getsum(int x) 
{   //   获取  1-x 的前缀和
	int ans = 0;
	for (int i = x; i; i -= lowbit(i)) ans += c[i];
	return ans;
}

int f[N];
int fa[N], dep[N], head[N], siz[N], son[N];
int top[N], dfn[N], dfnr[N];
int idx, cnte = 0;
struct Edge{
    int to, nxt;
} e[N << 1];


vector<int> vv;

void addedge(int u,int v)
{
    e[++cnte].to = v;
    e[cnte].nxt = head[u];
    head[u] = cnte;
}

void dfs1(int u, int far)
{
    fa[u] = far;
    dep[u] = dep[far] + 1;
    siz[u] = 1;
    dfn[u] = ++idx;
    son[u] = 0;
    for (int i = head[u]; i;i = e[i].nxt)
    {
        int to = e[i].to;
        if(to != far)
        {
            dfs1(to, u);
            siz[u] += siz[to];
            if(son[u] == 0 || siz[son[u]] < siz[to]) son[u] = to;
        }
    }
    dfnr[u] = idx;
}

void dfs2(int u, int tfar)
{
    top[u] = tfar;
    if(son[u]) dfs2(son[u], tfar);
    for (int i = head[u]; i;i = e[i].nxt)
    {
        if(e[i].to != son[u] && e[i].to != fa[u]) dfs2(e[i].to, e[i].to);
    }
   
}

int find(int x)
{
    if(x != f[x]) return f[x] = find(f[x]);
    return x;
}

void unio(int x, int y)
{
    int fx = find(x);
    int fy = find(y);
    if(fx != fy) f[fx] = fy;
}

int LCA(int x, int y)
{
    while(top[x] != top[y])
    {
        dep[top[x]] > dep[top[y]] ? x = fa[top[x]] : y = fa[top[y]];
    }
    return dep[x] < dep[y] ? x : y;
}

void merge(int x, int y)
{
    int fx = find(x), fy = find(y);
    if(fx == fy) return;
    int lca = LCA(x, y);
    while(find(x) != find(lca))
    {
        fx = find(x);
        add(dfn[fx], 1, idx);
        add(dfn[fx] + siz[fx], -1, idx);
        unio(fx, fa[fx]);
    }
    while(find(y) != find(lca))
    {
        fy = find(y);
        add(dfn[fy], 1, idx);
        add(dfn[fy] + siz[fy], -1, idx);
        unio(fy, fa[fy]);
    }
}

void init()
{
    cnte = 0;
    idx = 0;
    memset(c, 0, sizeof(c));
    for (int i = 1; i <= n;i++) f[i] = i, head[i] = 0;
    vv.clear();
}



int main()
{
    scanf("%d", &t);
    for (int cas = 1; cas <= t;cas++)
    {
        scanf("%d %d", &n, &m);
        init();
        for (int i = 1; i <= m; i++)
        {
            scanf("%d %d", &u, &v);
            if(find(u) != find(v))
            {
                unio(u, v);
                addedge(u, v);
                addedge(v, u);
            }
            else
            {
                vv.push_back(u);
                vv.push_back(v);
            }
        }

        dfs1(1, 0);
        dfs2(1, 1);

        for (int i = 1; i <= n;i++ ) f[i] = i;
        for (int i = 0; i < vv.size(); i += 2) merge(vv[i], vv[i + 1]);

        scanf("%d", &q);
        printf("Case #%d:\n", cas);
        while(q--)
        {
            scanf("%d %d %d", &op, &u, &v);
            if(op == 1)
            {
                merge(u, v);
            }
            else
            {
                int lca = LCA(u, v);
                ll ans = (dep[u] + dep[v] - dep[lca] * 2) - (getsum(dfn[u]) + getsum(dfn[v]) - 2 * getsum(dfn[lca])) ;
                printf("%lld\n", ans);
            }
        }
    }
    return 0;
}
posted @ 2020-09-19 13:57  Cyan_Cloud  阅读(147)  评论(0编辑  收藏  举报