1. 程式人生 > >[Luogu P3206] [BZOJ 2001] [HNOI2010]城市建設

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

洛谷傳送門

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),