[Luogu P3206] [BZOJ 2001] [HNOI2010]城市建设

版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/84432216

洛谷传送门

BZOJ传送门

描述

PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。

Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。

输入输出格式

输入格式:

文件第一行包含三个整数 N , M , Q N,M,Q ,分别表示城市的数目,可以修建的道路个数,及收到的消息个数。 接下来 M M 行,第 i + 1 i+1 行有三个用空格隔开的整数 X i , Y i , Z i ( 1 X i , Y i n , 0 Z i 50000000 ) X_i,Y_i,Z_i(1\le X_i,Y_i\le n, 0\le Z_i\le 50000000) ,表示在城市 X i X_i 与城市 Y i Y_i 之间修建道路的代价为 Z i Z_i 。接下来 Q Q 行,每行包含两个数 k , d k,d ,表示输入的第 k k 个道路的修建代价修改为 d d (即将 Z k Z_k 修改为 d d )。

输出格式:

输出包含 Q Q 行,第 i i 行输出得知前 i i 条消息后使城市连通的最小花费总和。

输入输出样例

输入样例#1:

5 5 3
1 2 1
2 3 2
3 4 3
4 5 4
5 1 5
1 6
1 1
5 3

输出样例#1:

14
10
9

说明

【数据规模】

对于20%的数据, n 1000 , m 6000 , Q 6000 n\le 1000,m\le 6000,Q\le 6000

有20%的数据, n 1000 , m 50000 , Q 8000 n\le 1000,m\le 50000,Q\le 8000 ,修改后的代价不会比之前的代价低。

对于100%的数据, n 20000 , m 50000 , Q 50000 n\le 20000,m\le 50000,Q\le 50000

解题分析

蒟蒻看来 C D Q CDQ 题解表示还是一脸懵逼, 只好学了一发线段树分治, 然后用 L C T LCT 搞了 80 p t s 80pts …(常数实在太大, 根本卡不动QAQ…)

线段树分治其实是为了处理修改/插入操作不方便的情况, 转化为只有删除/插入的操作。

比如在这道题中, L C T LCT 的修改操作十分不方便, 因为我们可以把修改操作视为删除后再加入, 而动态生成树并不能直接删边(听说有个叫做动态图的玩意可以搞?), 那么我们就不管这个删除操作, 直接算出每条边每种权值的出现时间段, 在一棵下标为时间的线段树上打上插入标记。 最后 D F S DFS 这棵线段树, 到达叶节点的时候就得到了当前时间点的最小生成树。

当然我们需要一个栈, 保存每一个节点在操作的时候新 l i n k / c u t link/cut 的边, 然后在回溯的时候弹栈撤销即可。

L C T LCT f i n d r o o t findroot 太慢, 用可撤销的并查集启发式合并代替(其实也是一个栈, 保存更改的位置, 同样弹栈撤销)。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#define R register
#define IN inline
#define W while
#define ll long long
#define MX 100500
IN char gc()
{
    static const int buflen = 1e6;
    static char buf[buflen], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, buflen, stdin), p1 == p2) ? EOF : *p1++;
}
#define gc gc()
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
template <class T>
void out(T a)
{
    if (!a) return;
    out(a / 10);
    putchar('0' + a % 10);
}
int n, m, q, top1, top2, top, cnt;
struct Edge {int from, to, len, id;} edge[MX << 1];
struct Node {
    int son[2], fat, val, mx, pos;
    bool rev;
    IN void ini(R int v) {son[0] = son[1] = fat = 0; val = mx = v;}
} tree[MX];
struct OPT {int from, to, id, val, typ;} stack[MX];
struct DSU {int smal, big;} stk[MX];
struct EDGE {int to, nex;} e[MX * 32];
int pre[MX], bel[MX], sta[MX], siz[MX], rec[MX], head[MX << 1];
int find(R int now) {return bel[now] == now ? now : find(bel[now]);}
IN void add(R int from, R int to) {e[++cnt] = {to, head[from]}, head[from] = cnt;}
namespace LCT
{
    #define ls tree[now].son[0]
    #define rs tree[now].son[1]
    #define dad tree[now].fat
    IN bool get(R int now) {return tree[dad].son[1] == now;}
    IN bool nroot(R int now) {return tree[dad].son[0] == now || tree[dad].son[1] == now;}
    IN void pushup(R int now)
    {
        tree[now].pos = now, tree[now].mx = tree[now].val;
        if (ls) if (tree[ls].mx > tree[now].mx) tree[now].mx = tree[ls].mx, tree[now].pos = tree[ls].pos;
        if (rs) if (tree[rs].mx > tree[now].mx) tree[now].mx = tree[rs].mx, tree[now].pos = tree[rs].pos;
    }
    IN void pushrev(R int now) {std::swap(ls, rs), tree[now].rev ^= 1;}
    IN void pushdown(R int now) {if (tree[now].rev) pushrev(ls), pushrev(rs), tree[now].rev = false;}
    IN void rotate(R int now)
    {
        R int fa = dad, grand = tree[fa].fat;
        R bool dir = get(now);
        tree[fa].son[dir] = tree[now].son[dir ^ 1],
        tree[tree[now].son[dir ^ 1]].fat = fa;
        tree[now].fat = grand;
        if (nroot(fa)) tree[grand].son[get(fa)] = now;
        tree[now].son[dir ^ 1] = fa;
        tree[fa].fat = now;
        pushup(fa);
    }
    IN void splay(R int now)
    {
        int tmp = now, fa;
        sta[top = 1] = now;
        W (nroot(now)) now = sta[++top] = dad;
        W (top) pushdown(sta[top--]);
        now = tmp;
        W (nroot(now))
        {
            fa = dad;
            if (nroot(fa)) rotate(get(now) == get(fa) ? fa : now);
            rotate(now);
        }
        pushup(now);
    }
    IN void access(R int now)
    {
        for (R int x = 0; now; x = now, now = dad)
        splay(now), rs = x, pushup(now);
    }
    IN void makeroot(R int now) {access(now), splay(now), pushrev(now);}
    IN void split(R int x, R int y) {makeroot(x), access(y), splay(y);}
    IN void link(R int x, R int y) {makeroot(x), tree[x].fat = y;}
    IN void cut(R int x, R int y) {split(x, y), tree[y].son[0] = tree[x].fat = 0, pushup(y);}
    #undef ls
    #undef rs
    #undef dad
}
#define ls (now << 1)
#define rs (now << 1 | 1)
void insert(R int now, R int lef, R int rig, R int lb, R int rb, R int id)
{
    if (lef >= lb && rig <= rb) return add(now, id), void();
    int mid = lef + rig >> 1;
    if (lb <= mid) insert(ls, lef, mid, lb, rb, id);
    if (rb  > mid) insert(rs, mid + 1, rig, lb, rb, id);
}
void DFS(R int now, R int lef, R int rig, ll ans)
{
    using namespace LCT;
    R int a, b, x, y, id, tar;
    int tp1 = top1, tp2 = top2, tmp;
    for (R int i = head[now]; i; i = e[i].nex)
    {
        tmp = e[i].to;
        x = edge[tmp].from, y = edge[tmp].to;
        a = find(x), b = find(y);
        if (a ^ b) //Disconnected yet
        {
            ++top1, ++top2;
            if (siz[a] > siz[b]) stk[top2] = {b, a}, siz[a] += siz[b], bel[b] = a;
            else stk[top2] = {a, b}, siz[b] += siz[a], bel[a] = b;
            tree[n + edge[tmp].id].ini(edge[tmp].len),
            link(x, n + edge[tmp].id), link(n + edge[tmp].id, y),
            ans += edge[tmp].len,
            stack[top1] = {x, y, edge[tmp].id, edge[tmp].len, 1};
        }
        else
        {
            split(x, y);
            tar = tree[y].pos, id = tar - n;
            if (tree[y].mx > edge[tmp].len)
            {
                ans += edge[tmp].len - tree[y].mx,
                ++top1,
                stack[top1] = {edge[id].from, edge[id].to, id, tree[y].mx, 0},
                cut(edge[id].from, tar); cut(edge[id].to, tar),
                ++top1,
                tree[n + edge[tmp].id].ini(edge[tmp].len),
                link(x, n + edge[tmp].id), link(n + edge[tmp].id, y),
                stack[top1] = {x, y, edge[tmp].id, edge[tmp].len, 1};
            }
        }
    }
    int mid = lef + rig >> 1;
    if (lef == rig) out(ans), puts("");
    else DFS(ls, lef, mid, ans), DFS(rs, mid + 1, rig, ans);
    W (top2 > tp2)
    {
        bel[stk[top2].smal] = stk[top2].smal,
        siz[stk[top2].big] -= siz[stk[top2].smal],
        top2--;
    }
    W (top1 > tp1)
    {
        if (stack[top1].typ) cut(stack[top1].from, stack[top1].id + n), cut(stack[top1].to, stack[top1].id + n);
        else
        {
            tree[stack[top1].id + n].ini(stack[top1].val),
            link(stack[top1].from, stack[top1].id + n), link(stack[top1].to, stack[top1].id + n);
        }
        top1--;
    }
}
#undef ls
#undef rs
int main(void)
{
    int a, b, c, tt;
    in(n), in(m), in(q); tt = m;
    for (R int i = 1; i <= n; ++i) bel[i] = i, siz[i] = 1;
    for (R int i = 1; i <= m; ++i) in(edge[i].from), in(edge[i].to), in(edge[i].len), pre[i] = 1, edge[i].id = i, rec[i] = i;
    for (R int i = 1; i <= q; ++i)
    {
        in(a), in(b);
        if (i != 1) insert(1, 1, q, pre[a], i - 1, rec[a]);
        edge[++tt] = edge[a], edge[tt].len = b;
        pre[a] = i; rec[a] = tt;
    }
    for (R int i = 1; i <= m; ++i)
    insert(1, 1, q, pre[i], q, rec[i]);
    DFS(1, 1, q, 0);
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/84432216