[Luogu P3206] [BZOJ 2001] [HNOI2010]城市建設
洛谷傳送門
BZOJ傳送門
描述
PS國是一個擁有諸多城市的大國,國王Louis為城市的交通建設可謂絞盡腦汁。Louis可以在某些城市之間修建道路,在不同的城市之間修建道路需要不同的花費。
Louis希望建造最少的道路使得國內所有的城市連通。但是由於某些因素,城市之間修建道路需要的花費會隨著時間而改變,Louis會不斷得到某道路的修建代價改變的訊息,他希望每得到一條訊息後能立即知道使城市連通的最小花費總和, Louis決定求助於你來完成這個任務。
輸入輸出格式
輸入格式:
檔案第一行包含三個整數
,分別表示城市的數目,可以修建的道路個數,及收到的訊息個數。 接下來
輸出格式:
輸出包含 行,第 行輸出得知前 條訊息後使城市連通的最小花費總和。
輸入輸出樣例
輸入樣例#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%的資料, 。
有20%的資料, ,修改後的代價不會比之前的代價低。
對於100%的資料, 。
解題分析
蒟蒻看來 題解表示還是一臉懵逼, 只好學了一發線段樹分治, 然後用 搞了 …(常數實在太大, 根本卡不動QAQ…)
線段樹分治其實是為了處理修改/插入操作不方便的情況, 轉化為只有刪除/插入的操作。
比如在這道題中, 的修改操作十分不方便, 因為我們可以把修改操作視為刪除後再加入, 而動態生成樹並不能直接刪邊(聽說有個叫做動態圖的玩意可以搞?), 那麼我們就不管這個刪除操作, 直接算出每條邊每種權值的出現時間段, 在一棵下標為時間的線段樹上打上插入標記。 最後 這棵線段樹, 到達葉節點的時候就得到了當前時間點的最小生成樹。
當然我們需要一個棧, 儲存每一個節點在操作的時候新 的邊, 然後在回溯的時候彈棧撤銷即可。
的 太慢, 用可撤銷的並查集啟發式合併代替(其實也是一個棧, 儲存更改的位置, 同樣彈棧撤銷)。
程式碼如下:
#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),