Address
Solution
- 题目大概就是一个动态最小生成树,不强制在线。
- 容易想到用 CDQ分治 来处理。
- 考虑对一个修改的区间
[l,r]
[
l
,
r
]
:
- 先加上那些肯定要加的边(把
[l,r]
[
l
,
r
]
中要修改的边全部加上后做
Kruskal
K
r
u
s
k
a
l
,那些仍然可以被加上的边),则当我们处理区间
[l,r]
[
l
,
r
]
时,原图点的规模被缩成了
O(r−l)
O
(
r
−
l
)
;
- 再删去那些肯定不被加的边(把
[l,r]
[
l
,
r
]
中要修改的边都删去后做
Kruskal
K
r
u
s
k
a
l
,那些仍然不可以被加上的边),则当我们处理区间
[l,r]
[
l
,
r
]
时,原图边的规模被缩成了
O(r−l)
O
(
r
−
l
)
。
- 则当我们处理到单独一个操作时,原图点和边的规模都被缩成了
O(1)
O
(
1
)
,就可以直接做
Kruskal
K
r
u
s
k
a
l
了。
- 同样,每个分治的区间
[l,r]
[
l
,
r
]
处理的复杂度为
O((r−l)log(r−l))
O
(
(
r
−
l
)
log
(
r
−
l
)
)
,总复杂度
O(nlog2n)
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()
{
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);
return 0;
}