BZOJ2001 [HNOI2010]城市建设

Address


Solution

  • 题目大概就是一个动态最小生成树,不强制在线。
  • 容易想到用 CDQ分治 来处理。
  • 考虑对一个修改的区间 [ l , r ]
    1. 先加上那些肯定要加的边(把 [ l , r ] 中要修改的边全部加上后做 K r u s k a l ,那些仍然可以被加上的边),则当我们处理区间 [ l , r ] 时,原图点的规模被缩成了 O ( r l )
    2. 再删去那些肯定不被加的边(把 [ l , r ] 中要修改的边都删去后做 K r u s k a l ,那些仍然不可以被加上的边),则当我们处理区间 [ l , r ] 时,原图边的规模被缩成了 O ( r l )
  • 则当我们处理到单独一个操作时,原图点和边的规模都被缩成了 O ( 1 ) ,就可以直接做 K r u s k a l 了。
  • 同样,每个分治的区间 [ l , r ] 处理的复杂度为 O ( ( r l ) log ( r l ) ) ,总复杂度 O ( n log 2 n )
  • 要注意前面的操作会影响后面的答案,因此要处理左区间对右区间的影响。
  • 然后就是 恶心的 实现了。
  • 因为并查集要连边后再删除,直接做很不方便,记一个栈存储每次更改前的并查集父子关系,再逐一退栈还原回去。
  • 具体细节见代码。

Code

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

using namespace std;

typedef long long ll;
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(ll x)
    {
        if (x > 9) put(x / 10);
        outChar(x % 10 + 48);
    }   
};
using namespace inout;

const int N = 2e4 + 5, M = 5e4 + 5, L = 2e6 + 5;
int par[L], son[L], fa[N]; bool vis[M]; ll ans[M]; 
int w[M], u[M], v[M], k[M], d[M];
int n, m, q, top, a[L], b[L], typ[L];

inline int Find(int x)
{
    if (fa[x] != x)
    {
        ++top;
        par[top] = fa[x];
        son[top] = x;
        fa[x] = Find(fa[x]);
    }
    return fa[x];
}

inline bool Merge(int x, int y)
{
    int tx = Find(x),
        ty = Find(y);
    if (tx != ty)
    {
        ++top;
        par[top] = fa[tx];
        son[top] = tx;
        fa[tx] = ty;
        return true;
    }
    return false;
}

inline void Cut(int tis)
{
    while (top > tis)
        fa[son[top]] = par[top], --top;
}

inline bool cmp(const int &x, const int &y)
{
    return w[x] < w[y];
}

inline void CDQsolve(int l, int r, int tl, int tr)
{
    int mid = l + r >> 1, tis = top, wr = tr, p = 0;
    if (l == r)
    {
        int z = w[k[l]]; w[k[l]] = d[l];
        for (int i = tl; i <= tr; ++i) a[++p] = b[i];
        sort(a + 1, a + p + 1, cmp);
        for (int i = 1; i <= p; ++i)
            if (Merge(u[a[i]], v[a[i]])) ans[l] += w[a[i]];
        Cut(tis); w[k[l]] = z;
        return ;
    }

    for (int i = l; i <= r; ++i) vis[k[i]] = true;
    for (int i = tl; i <= tr; ++i)
        if (!vis[b[i]]) a[++p] = b[i];
    for (int i = 1; i <= p; ++i) typ[i] = 0;
    sort(a + 1, a + p + 1, cmp);

    for (int i = l; i <= r; ++i) Merge(u[k[i]], v[k[i]]);
    for (int i = 1; i <= p; ++i)
        if (Merge(u[a[i]], v[a[i]])) typ[i] = 1;
    Cut(tis);

    for (int i = 1; i <= p; ++i)
        if (!Merge(u[a[i]], v[a[i]])) typ[i] = 2;
    Cut(tis);

    ll del = 0;
    for (int i = 1; i <= p; ++i)
        if (!typ[i]) 
            b[++tr] = a[i];
        else if (typ[i] & 1)
            Merge(u[a[i]], v[a[i]]), del += w[a[i]];
    for (int i = l; i <= r; ++i) b[++tr] = k[i];
    for (int i = l; i <= r; ++i) vis[k[i]] = false;

    int ntis = top;
    CDQsolve(l, mid, wr + 1, tr);
    for (int i = l; i <= mid; ++i) w[k[i]] = d[i]; 
    Cut(ntis); 
    CDQsolve(mid + 1, r, wr + 1, tr);
    Cut(ntis);
    for (int i = l; i <= r; ++i) ans[i] += del; 
}

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

    n = get(); m = get(); q = get();
    for (int i = 1; i <= m; ++i)
        u[i] = get(), v[i] = get(), w[i] = get();
    for (int i = 1; i <= q; ++i)
        k[i] = get(), d[i] = get(); 
    for (int i = 1; i <= m; ++i) b[i] = i;
    for (int i = 1; i <= n; ++i) fa[i] = i;

    CDQsolve(1, q, 1, m);
    for (int i = 1; i <= q; ++i)
        put(ans[i]), outChar('\n');
    fwrite(fwt, 1, ohed - fwt, stdout);

//  fclose(stdin); fclose(stdout);
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/bzjr_log_x/article/details/79679870
今日推荐