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