bzoj 5415 [Noi2018]归程 可持久化并查集/kruskal重构树
题面
解法
先看一看部分分
1-6
直接用dijkstra求最短路,然后判断一下即可
时间复杂度:\(O((n+m)\ log\ n)\)
期望得分:\(30\)
7-11
倍增求出路径上的最小值,然后比较一下即可
时间复杂度:\(O(q\ log\ n)\)
期望得分:\(30+25=55\)
12-14
发现可以离线
将所有边按照海拔从大到小排序,询问按照海拔从大到小排序
那么就可以转化成不断加边,然后求\(v\)所在连通块中到1距离最短的是多少
直接并查集即可
时间复杂度:\(O(q\alpha(n))\)
期望得分:\(30+25+15=70\)
15-16
直接暴力即可
时间复杂度:\(O(nq)\)
期望得分:\(30+25+15+10=80\)
代码就不给了,太长了
100分算法
根据离线的那一部分,我们发现,因为每一次的起始点是不固定的,且强制在线,所以不方便维护
直接可持久化并查集!!!
时间复杂度:\(O(q\ log^2\ n)\)
md代码复杂度巨大,时间好不容易才苟过去
代码(可持久化并查集)
#include <bits/stdc++.h>
#define PI pair <int, int>
#define mp make_pair
#define N 200010
using namespace std;
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
int lc, rc, fa, mn, siz;
} t[N * 60];
struct Edge {
int x, y, v, p;
bool operator < (const Edge &a) const {
return p > a.p;
}
} a[N * 2];
struct Info {
int next, num, v, p;
} e[N * 6];
struct Ret {
int first, second, third;
};
int n, m, cnt, tot, used[N], dis[N], rt[N * 2], b[N * 2];
void add(int x, int y, int v, int p) {
e[++cnt] = (Info) {e[x].next, y, v, p};
e[x].next = cnt;
}
void dijkstra(int s) {
for (int i = 1; i <= n; i++)
dis[i] = 1 << 30, used[i] = 0;
priority_queue <PI, vector <PI>, greater <PI> > h;
dis[s] = 0; h.push(mp(0, s));
while (!h.empty()) {
PI tmp = h.top(); h.pop();
int x = tmp.second;
if (used[x]) continue; used[x] = 1;
for (int p = e[x].next; p; p = e[p].next) {
int k = e[p].num, v = e[p].v;
if (dis[k] > dis[x] + v) {
dis[k] = dis[x] + v;
h.push(mp(dis[k], k));
}
}
}
}
void build(int &k, int l, int r) {
k = ++tot;
if (l == r) {
t[k].fa = l, t[k].mn = dis[l], t[k].siz = 1;
return;
}
int mid = (l + r) >> 1;
build(t[k].lc, l, mid), build(t[k].rc, mid + 1, r);
}
void ins(int &k, int cur, int l, int r, int x, int fa, int mn, int siz) {
k = ++tot; t[k] = t[cur];
if (l == r) {t[k].fa = fa, t[k].mn = mn, t[k].siz = siz; return;}
int mid = (l + r) >> 1;
if (x <= mid) ins(t[k].lc, t[cur].lc, l, mid, x, fa, mn, siz);
else ins(t[k].rc, t[cur].rc, mid + 1, r, x, fa, mn, siz);
}
Ret query(int k, int l, int r, int x) {
if (l == r) return (Ret) {t[k].fa, t[k].mn, t[k].siz};
int mid = (l + r) >> 1;
if (x <= mid) return query(t[k].lc, l, mid, x);
return query(t[k].rc, mid + 1, r, x);
}
int Find(int now, int x) {
int fa = x;
while (true) {
Ret tx = query(rt[now], 1, n, fa);
if (tx.first == fa) return fa;
fa = tx.first;
}
}
void merge(int t, int x, int y) {
int tx = Find(t, x), ty = Find(t, y);
if (tx == ty) return;
Ret a = query(rt[t], 1, n, tx), b = query(rt[t], 1, n, ty);
int sx = a.third, sy = b.third;
if (sx > sy) swap(tx, ty), swap(sx, sy);
ins(rt[t], rt[t], 1, n, tx, ty, a.second, sx);
ins(rt[t], rt[t], 1, n, ty, ty, min(a.second, b.second), sx + sy);
}
int calc(int n, int v) {
int l = 1, r = n, ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (b[mid] > v) ans = mid, l = mid + 1;
else r = mid - 1;
}
return ans;
}
int main() {
int T; read(T);
while (T--) {
read(n), read(m); tot = 0; cnt = n;
for (int i = 1; i <= n; i++) e[i].next = 0;
for (int i = 1; i <= m; i++) rt[i] = 0;
for (int i = 1; i <= m; i++) {
int x, y, v, p;
read(x), read(y), read(v), read(p);
a[i] = (Edge) {x, y, v, p}; b[i] = p;
add(x, y, v, p), add(y, x, v, p);
}
dijkstra(1);
sort(a + 1, a + m + 1);
sort(b + 1, b + m + 1);
int l = unique(b + 1, b + m + 1) - b - 1;
reverse(b + 1, b + l + 1);
build(rt[0], 1, n);
for (int i = 1, j = 1; i <= l; i++) {
rt[i] = rt[i - 1];
for (; a[j].p == b[i]; j++) {
int x = a[j].x, y = a[j].y;
merge(i, x, y);
}
}
int q, k, s, ans = 0;
read(q), read(k), read(s);
while (q--) {
int x, p; read(x), read(p);
x = (x + k * ans - 1) % n + 1, p = (p + k * ans) % (s + 1);
int t = calc(l, p), tx = Find(t, x);
Ret tmp = query(rt[t], 1, n, tx); ans = tmp.second;
cout << ans << "\n";
}
memset(t, 0, (tot + 1) * 2);
}
return 0;
}
100分(kruskal重构树)
这是个什么东西呢??
就是kruskal算法中合并两个连通块的时候,建一个虚拟节点,权值为边权,然后两个儿子为两个连通块的根
这样我们就可以每一次直接在这棵树上倍增,求子树最小值了
时间复杂度:\(O(q\ log\ n)\)
代码
#include <bits/stdc++.h>
#define int long long
#define PI pair <int, int>
#define mp make_pair
#define N 400010
using namespace std;
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
int x, y, v, p;
bool operator < (const Node &a) const {
return p > a.p;
}
} a[N];
struct Edge {
int next, num, v;
} e[N * 3];
int n, m, cnt, dis[N], used[N], v[N], p[N], f[N][21];
vector <int> E[N];
void add(int x, int y, int v) {
e[++cnt] = (Edge) {e[x].next, y, v};
e[x].next = cnt;
}
void dijkstra(int s) {
for (int i = 1; i <= 2 * n; i++)
dis[i] = LONG_LONG_MAX, used[i] = 0;
priority_queue <PI, vector <PI>, greater <PI> > h;
dis[s] = 0; h.push(mp(0, s));
while (!h.empty()) {
PI tmp = h.top(); h.pop();
int x = tmp.second;
if (used[x]) continue; used[x] = 1;
for (int p = e[x].next; p; p = e[p].next) {
int k = e[p].num, v = e[p].v;
if (dis[k] > dis[x] + v)
dis[k] = dis[x] + v, h.push(mp(dis[k], k));
}
}
}
int Find(int x) {
if (p[x] == x) return x;
return p[x] = Find(p[x]);
}
int query(int x, int p) {
for (int i = 20; i >= 0; i--)
if (f[x][i] && v[f[x][i]] > p) x = f[x][i];
return dis[x];
}
main() {
int T; read(T);
while (T--) {
read(n), read(m); cnt = n;
for (int i = 1; i <= n; i++) e[i].next = 0;
for (int i = 1; i <= m; i++) {
int x, y, v, p;
read(x), read(y), read(v), read(p);
add(x, y, v), add(y, x, v);
a[i] = (Node) {x, y, v, p};
}
sort(a + 1, a + m + 1);
dijkstra(1); int tot = n;
memset(f, 0, sizeof(f));
for (int i = 1; i <= 2 * n; i++)
p[i] = i, v[i] = -1, E[i].clear();
for (int i = 1; i <= m; i++) {
int tx = Find(a[i].x), ty = Find(a[i].y), tp = a[i].p;
if (tx != ty) {
p[tx] = p[ty] = ++tot, v[tot] = tp;
E[tot].push_back(tx), E[tot].push_back(ty);
dis[tot] = min(dis[tx], dis[ty]);
f[tx][0] = tot, f[ty][0] = tot;
}
}
for (int j = 1; j <= 20; j++)
for (int i = 1; i <= tot; i++)
if (f[i][j - 1]) f[i][j] = f[f[i][j - 1]][j - 1];
int q, k, s, ans = 0;
read(q), read(k), read(s);
while (q--) {
int x, p; read(x), read(p);
x = (x + k * ans - 1) % n + 1, p = (p + k * ans) % (s + 1);
ans = query(x, p); cout << ans << "\n";
}
}
return 0;
}