[APIO2019]桥梁(分块+并查集)
题目:洛谷P5443、LOJ#3145
题目描述:
给定\(n\)个点,\(m\)条边,初始每条边都有边权,\(q\)次操作
每次操作为以下两种之一:
- 修改:改变一条边的边权
- 询问:求以某个点为起始点,只能经过大小不小于某个值的边,求能到达多少个不同的点
\(n \leq 50000\),\(m,q \leq 100000\),所有的边权长度不超过\(10^{9}\)
蒟蒻题解:
法一:
修改就直接修改,询问时把所有边权大于等于给定值的边取出来跑并查集
时间复杂度\(\Theta(m \cdot q \cdot α(m))\)
期望得分:\(13\)分
法二:
离线,由于每次询问都是经过大小不小于某个值的边,所以可以把所有的操作按大到小排序,对于永远不会被修改的边可以直接添加到并查集里
每次处理询问时,就找所有可能会被修改的边加进可撤销并查集中,处理完这个询问后再撤销
时间复杂度\(\Theta(m log\ m + q^{2} log\ m)\)
期望得分:\(13\)分
法三:
法一的优点是询问小时很快,法二的优点是修改小时很快
考虑结合以上两个算法的优点,对操作进行分块,设每个块的大小为\(S\)
对于一个块,把所有的操作按大到小排序,对于这个块内永远不会被修改的边可以直接添加到并查集里,然后每次处理询问时,就找所有可能会被修改的边加进可撤销并查集中,处理完这个询问后再撤销(同法二)
时间复杂度\(\Theta(\frac{q}{S} \cdot (m log\ m + S^{2} log\ m)) = \Theta(\frac{q \cdot m log\ m}{S} + q \cdot S \cdot log\ m)\)
考虑进一步优化,由于每次修改的边数不超过\(S\),对于不会被修改的边用的并查集用路径压缩,然后会被修改的边直接在图中连边,后面可以直接暴力\(dfs\)去找,遍历到的点数一定不超过\(S\)
时间复杂度\(\Theta(\frac{q}{S} \cdot (m log\ m + S^{2} α(m))) = \Theta(\frac{q \cdot m log\ m}{S} + q \cdot S \cdot α(m))\)
令\(\frac{q \cdot m log\ m}{S} = q \cdot S \cdot α(m)\),解得\(S = \sqrt{\frac{m log\ m}{α(m)}}\)
然后我就试着取了\(S = \sqrt{m log\ m}\),结果洛谷开只有\(12\)分,\(LOJ\)开\(O2\)只有\(73\)分(开\(Ofast\)好像也一样)代码自带大常数\kk
后面看了一下别人程序取的块的大小,看到有人取\(\sqrt{10q}\),我就试了下,结果在\(LOJ\)开\(Ofast\)后过了,洛谷开\(O2\)后也过了(但是洛谷不开\(O2\)只有\(8\)分)
参考程序:
#include<bits/stdc++.h>
using namespace std;
#define Re register int
const int N = 50005, M = 100005;
struct info
{
int l, r, s, id;
}a[M], qu[M], d[M];
int n, m, q, S = 1, res, t, cnt, tot, g[N], fa[N], siz[N], ans[N], ds[M], tim[M], hea[N], nxt[N], to[N];
bool b[M], p[N];
inline int read()
{
char c = getchar();
int ans = 0;
while (c < 48 || c > 57) c = getchar();
while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
return ans;
}
inline void write(int x)
{
int num = 0;
char sc[15];
while (x) sc[++num] = x % 10 + 48, x /= 10;
while (num) putchar(sc[num--]);
putchar('\n');
}
inline bool cmp(info x, info y)
{
return x.s > y.s;
}
inline bool cmp1(info x, info y)
{
return x.id < y.id;
}
inline int find_fa(int x)
{
return (fa[x] ^ x) ? fa[x] = find_fa(fa[x]) : x;
}
inline void add(int x, int y)
{
nxt[++cnt] = hea[x], to[cnt] = y, hea[x] = cnt;
}
inline void dfs(int x)
{
p[x] = 1, tot += siz[x];
for (Re i = hea[x]; i; i = nxt[i])
{
int u = to[i];
if (!p[u]) dfs(u);
}
}
inline void clea(int x)
{
p[x] = 0;
for (Re i = hea[x]; i; i = nxt[i])
{
int u = to[i];
if (p[u]) clea(u);
}
}
int main()
{
n = read(), m = read();
for (Re i = 1; i <= m; ++i) a[i].l = read(), a[i].r = read(), a[i].s = read(), a[i].id = i;
q = read(), S = min(q, (int)sqrt(q * 10));
for (Re i = 1, j; i <= q; i = j + 1)
{
j = min(i + S, q), res = t = 0;
int u = j - i + 1;
for (Re k = 1; k <= n; ++k) fa[k] = k, siz[k] = 1;
for (Re k = 0; k < u; ++k)
{
qu[k].l = read(), qu[k].r = read(), qu[k].s = read(), qu[k].id = k;
if (qu[k].l & 1) b[qu[k].r] = 1;
}
for (Re k = 1; k <= m; ++k)
if (!b[k]) d[res].l = a[k].l, d[res].r = a[k].r, d[res++].s = a[k].s;
else g[t++] = k;
sort(qu, qu + u, cmp), sort(d, d + res, cmp);
for (Re k = 0, l = 0; k < u; ++k)
{
while (l < res && d[l].s >= qu[k].s)
{
int v = find_fa(d[l].l), w = find_fa(d[l].r);
if (v ^ w) siz[v] += siz[w], fa[w] = v;
++l;
}
if (qu[k].l & 1) continue;
cnt = tot = 0;
for (Re r = 0; r < t; ++r) ds[g[r]] = a[g[r]].s, tim[g[r]] = -1;
for (Re r = 0; r < u; ++r)
if ((qu[r].l & 1) && qu[r].id < qu[k].id && qu[r].id > tim[qu[r].r]) ds[qu[r].r] = qu[r].s, tim[qu[r].r] = qu[r].id;
for (Re r = 0; r < t; ++r)
if (ds[g[r]] >= qu[k].s) add(find_fa(a[g[r]].l), find_fa(a[g[r]].r)), add(fa[a[g[r]].r], fa[a[g[r]].l]);
dfs(find_fa(qu[k].r)), clea(fa[qu[k].r]);
for (Re r = 0; r < t; ++r)
if (ds[g[r]] >= qu[k].s) hea[fa[a[g[r]].l]] = hea[fa[a[g[r]].r]] = 0;
ans[qu[k].id] = tot;
}
sort(qu, qu + u, cmp1);
for (Re k = 0; k < u; ++k)
if (qu[k].l & 1) a[qu[k].r].s = qu[k].s, b[qu[k].r] = 0;
else write(ans[k]);
}
return 0;
}

浙公网安备 33010602011771号