1. 程式人生 > 實用技巧 >hdu 6200 mustedge mustedge mustedge(樹鏈剖分 + 樹狀陣列 + 並差集)

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;
}