洛谷传送门
BZOJ传送门
描述
PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。
Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。
输入输出格式
输入格式:
文件第一行包含三个整数 N , M , Q N,M,Q N,M,Q,分别表示城市的数目,可以修建的道路个数,及收到的消息个数。 接下来 M M M行,第 i + 1 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) Xi,Yi,Zi(1≤Xi,Yi≤n,0≤Zi≤50000000),表示在城市 X i X_i Xi与城市 Y i Y_i Yi之间修建道路的代价为 Z i Z_i Zi。接下来 Q Q Q行,每行包含两个数 k , d k,d k,d,表示输入的第 k k k个道路的修建代价修改为 d d d(即将 Z k Z_k Zk修改为 d d d)。
输出格式:
输出包含 Q Q Q行,第 i i 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 n≤1000,m≤6000,Q≤6000。
有20%的数据, n ≤ 1000 , m ≤ 50000 , Q ≤ 8000 n\le 1000,m\le 50000,Q\le 8000 n≤1000,m≤50000,Q≤8000,修改后的代价不会比之前的代价低。
对于100%的数据, n ≤ 20000 , m ≤ 50000 , Q ≤ 50000 n\le 20000,m\le 50000,Q\le 50000 n≤20000,m≤50000,Q≤50000。
解题分析
蒟蒻看来 C D Q CDQ CDQ题解表示还是一脸懵逼, 只好学了一发线段树分治, 然后用 L C T LCT LCT搞了 80 p t s 80pts 80pts…(常数实在太大, 根本卡不动QAQ…)
线段树分治其实是为了处理修改/插入操作不方便的情况, 转化为只有删除/插入的操作。
比如在这道题中, L C T LCT LCT的修改操作十分不方便, 因为我们可以把修改操作视为删除后再加入, 而动态生成树并不能直接删边(听说有个叫做动态图的玩意可以搞?), 那么我们就不管这个删除操作, 直接算出每条边每种权值的出现时间段, 在一棵下标为时间的线段树上打上插入标记。 最后 D F S DFS DFS这棵线段树, 到达叶节点的时候就得到了当前时间点的最小生成树。
当然我们需要一个栈, 保存每一个节点在操作的时候新 l i n k / c u t link/cut link/cut的边, 然后在回溯的时候弹栈撤销即可。
L C T LCT LCT的 f i n d r o o t findroot 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);
}