BZOJ2896 桥

Address


Solution

  • 容易想到一种暴力做法。
  • 每次删边后,把原图的边双连通分量缩点,原图变为一颗树。
  • 令树上的边权均为 1,则 A,B A , B 点间桥的数量为 A,B A , B 点所在的边双连通分量在树上的距离。
  • 设询问数为 Q Q , 时间复杂度 O(Q(N+M)),显然不能通过。
  • 考虑动态地改变这棵树。
  • 如果正序做会麻烦,因为我们需要把树上的一个点拆成一些点后再重新连边。
  • 考虑倒序做,原来删边的操作变为加边,即需要把树上的一条路径上的点合并成一个点。
  • 则对于一个加边操作 (A,B) ( A , B ) ,合并就相当于把 A,B A , B 点在树上的路径上的每一条边权都修改为 0,这显然可以用树链剖分实现。
  • 时间复杂度 O(N+M+Qlog2N) O ( N + M + Q log 2 ⁡ N )

Code

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <map>

using namespace std;

namespace inout
{
    const int S = 1 << 20;
    char frd[S], *ihed = frd + S;
    const char *ital = ihed;

    inline char inChar()
    {
        if (ihed == ital)
            fread(frd, 1, S, stdin), ihed = frd;
        return *ihed++;
    }

    inline int get()
    {
        char ch; int res = 0; bool flag = false;
        while (!isdigit(ch = inChar()) && ch != '-');
        (ch == '-' ? flag = true : res = ch ^ 48);
        while (isdigit(ch = inChar()))
            res = res * 10 + ch - 48;
        return flag ? -res : res; 
    }

    char fwt[S], *ohed = fwt;
    const char *otal = ohed + S;

    inline void outChar(char ch)
    {
        if (ohed == otal)   
            fwrite(fwt, 1, S, stdout), ohed = fwt;
        *ohed++ = ch;
    }

    inline void put(int x)
    {
        if (x > 9) put(x / 10);
        outChar(x % 10 + 48);
    }   
};
using namespace inout;

const int N = 5e4 + 5, M = 2e5 + 5, L = N << 2, P = 4e5 + 5;

map<int, bool> ma[N];
int lxt[M], lo[M], lst[N], rxt[M], ro[M], rst[N];
int dfn[N], low[N], f[N], col[N]; bool cut[M], stp[M];
int top[N], pos[N], son[N], sze[N], dep[N], fa[N];
bool tag[L]; int sum[L], ans[P], a[P], b[P], c[P];
int n, m, T = 1, Q, q, tis, num;

inline void Link(int x, int y)
{
    lxt[++T] = lst[x]; lst[x] = T; lo[T] = y;
    lxt[++T] = lst[y]; lst[y] = T; lo[T] = x;
}

inline void Rink(int x, int y)
{
    rxt[++Q] = rst[x]; rst[x] = Q; ro[Q] = y;
}

inline void CkMin(int &x, int y)
{
    if (x > y) x = y;   
}

inline void Tarjan(int x, int fa)
{
    dfn[x] = low[x] = ++tis;
    for (int i = lst[x]; i; i = lxt[i])
    {
        int y = lo[i];
        if (stp[i] || y == fa) continue;
        if (!dfn[y])
        {
            Tarjan(y, x);
            CkMin(low[x], low[y]);
            if (dfn[x] < low[y])
                cut[i] = cut[i ^ 1] = true;
        }
        else CkMin(low[x], dfn[y]);
    }
}

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

inline void Merge(int x, int y)
{
    int tx = Find(x),
        ty = Find(y);
    if (tx != ty) f[tx] = ty;
}

inline void Dfs1(int x, int Fa)
{
    dep[x] = dep[Fa] + 1; sze[x] = 1;
    for (int i = rst[x]; i; i = rxt[i])
    {
        int y = ro[i];
        if (y == Fa) continue;
        fa[y] = x; Dfs1(y, x); sze[x] += sze[y];
        if (sze[y] > sze[son[x]]) son[x] = y;
    }
}

inline void Dfs2(int x)
{
    if (son[x])
    {
        top[son[x]] = top[x];
        pos[son[x]] = ++num;
        Dfs2(son[x]);
    }
    for (int i = rst[x]; i; i = rxt[i])
    {
        int y = ro[i];
        if (top[y]) continue;
        top[y] = y;
        pos[y] = ++num;
        Dfs2(y);
    }
}

inline void Init()
{
    Dfs1(col[1], 0);
    top[col[1]] = pos[col[1]] = num = 1;
    Dfs2(col[1]);
}

#define sL s << 1
#define sR s << 1 | 1

inline void Uptdate(int s)
{
    sum[s] = sum[sL] + sum[sR];
}

inline void addTag(int s)
{
    tag[s] = 1; sum[s] = 0;
}

inline void pushDown(int s)
{
    if (tag[s])
    {
        addTag(sL);
        addTag(sR);
        tag[s] = 0;
    }
}

inline void Build(int s, int l, int r)
{
    if (l == r) return (void)(l == 1 ? sum[s] = 0 : sum[s] = 1);
    int mid = l + r >> 1;
    Build(sL, l, mid); Build(sR, mid + 1, r);
    Uptdate(s);
}

inline void Modify(int s, int l, int r, int x, int y)
{
    if (l == x && r == y) return addTag(s);
    pushDown(s);
    int mid = l + r >> 1;
    if (y <= mid)
        Modify(sL, l, mid, x, y);
    else if (x > mid)
        Modify(sR, mid + 1, r, x, y);
    else 
    {
        Modify(sL, l, mid, x, mid);
        Modify(sR, mid + 1, r, mid + 1, y);
    }
    Uptdate(s);
}

inline int Query(int s, int l, int r, int x, int y)
{
    if (l == x && r == y) return sum[s];
    pushDown(s);
    int mid = l + r >> 1;
    if (y <= mid)   
        return Query(sL, l, mid, x, y);
    else if (x > mid)
        return Query(sR, mid + 1, r, x, y);
    else 
        return Query(sL, l, mid, x, mid)
             + Query(sR, mid + 1, r, mid + 1, y);
}

inline int pathModify(int x, int y)
{
    if (dep[x] < dep[y]) swap(x, y);
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x, y); 
        Modify(1, 1, num, pos[top[x]], pos[x]); 
        x = fa[top[x]];
    }
    if (dep[x] < dep[y]) swap(x, y);
    if (x != y) Modify(1, 1, num, pos[y] + 1, pos[x]);
}

inline int pathQuery(int x, int y)
{
    if (dep[x] < dep[y]) swap(x, y);
    int res = 0;
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        res += Query(1, 1, num, pos[top[x]], pos[x]);
        x = fa[top[x]];
    }
    if (dep[x] < dep[y]) swap(x, y);
    if (x != y) res += Query(1, 1, num, pos[y] + 1, pos[x]);
    return res;
}

int main()
{
//  freopen("bridge.in", "r", stdin);
//  freopen("bridge.out", "w", stdout);

    n = get(); m = get(); 
    for (int i = 1; i <= m; ++i) Link(get(), get());

    c[++q] = get();
    while (c[q] != -1)
        a[q] = get(), b[q] = get(), c[++q] = get();
    --q;
    for (int i = 1; i <= q; ++i) 
        if (!c[i]) ma[a[i]][b[i]] = ma[b[i]][a[i]] = true;
    for (int i = 1; i <= n; ++i)
        for (int j = lst[i]; j; j = lxt[j])
            if (ma[i][lo[j]]) stp[j] = true; 
    for (int i = 1; i <= n; ++i) f[i] = i;
    for (int i = 1; i <= n; ++i)
        if (!dfn[i]) Tarjan(i, 0);
    for (int i = 1; i <= n; ++i)
        for (int j = lst[i]; j; j = lxt[j])
            if (!cut[j] && !stp[j]) Merge(i, lo[j]);
    for (int i = 1; i <= n; ++i) col[i] = Find(i);
    for (int i = 1; i <= n; ++i)
        for (int j = lst[i]; j; j = lxt[j])
        {
            int x = col[i], y = col[lo[j]];
            if (x != y && !stp[j]) Rink(x, y);
        }
    Init(); Build(1, 1, num);
    for (int i = q; i >= 1; --i)
        if (!c[i]) pathModify(col[a[i]], col[b[i]]);
            else ans[i] = pathQuery(col[a[i]], col[b[i]]);
    for (int i = 1; i <= q; ++i)
        if (c[i]) put(ans[i]), outChar('\n');

    fwrite(fwt, 1, ohed - fwt, stdout);
//  fclose(stdin); fclose(stdout);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值