[bzoj3995][SDOI2015]道路修建-线段树
Brief Description
某国有2N个城市,这2N个城市构成了一个2行N列的方格网。现在该国政府有一个旅游发展计划,这个计划需要选定L、R两列(L<=R),修建若干条专用道路,使得这两列之间(包括这两列)的所有2(R-L+1)个城市中每个城市可以只通过专用道路就可以到达这2(R-L+1)个城市中的任何一个城市。这种专用道路只能在同一行相邻两列的城市或者同一列的两个城市之间修建,且修建需要花费一定的费用。由于该国政府决定尽量缩减开支,因此政府决定,选定L、R后,只修建2(R-L+1)-1条专用道路,使得这些专用道路构成一个树结构。现在你需要帮助该国政府写一个程序,完成这个任务。具体地,该任务包含M个操作,每个操作的格式如下:
-
C x0 y0 x1 y1 w:由于重新对第x0行第y0列的城市和第x1行第y1列的城市之间的情况进行了考察,它们之间修建一条专用道路的花费变成了w;
-
Q L R:若政府选定的两列分别为L、R,询问政府的最小开支。
Algorithm Design
我们考虑使用线段树维护区间\([l,r]\)的最小生成树.
考察区间合并:
可以发现, 区间合并时一共有五种本质不同的情况:
其中相同颜色的是同一个联通块.
然后合并的时候有两种选择, 一种是加一条边, 一种是加两条边, 枚举所有合并情况就好了, 维护这些所有的状态.
其实这个题目和SHOI某年一个题非常相似.
Code
#include <algorithm>
#include <cstdio>
#include <cstring>
const int maxn = 60110;
int l[maxn][2], r[maxn][2], u[maxn], d[maxn];
int N, M;
struct seg {
int a[5];
int l, r;
} t[maxn << 2];
void up(int &a, int b) { a = std::min(a, b); }
void update(int a[5], int l[5], int r[5], int se, int de) {
memset(a, 0x3f, sizeof(t[0].a));
up(a[0], l[0] + r[0] + se);
up(a[1], l[0] + r[1] + se);
up(a[0], l[0] + r[2] + de);
up(a[0], l[0] + r[3] + de);
up(a[1], l[0] + r[3] + se);
up(a[1], l[0] + r[4] + de);
up(a[0], l[1] + r[0] + de);
up(a[1], l[1] + r[1] + de);
up(a[1], l[1] + r[3] + de);
up(a[2], l[2] + r[0] + se);
up(a[4], l[2] + r[1] + se);
up(a[2], l[2] + r[2] + de);
up(a[4], l[2] + r[3] + se);
up(a[2], l[2] + r[3] + de);
up(a[4], l[2] + r[4] + de);
up(a[2], l[3] + r[0] + se);
up(a[0], l[3] + r[0] + de);
up(a[4], l[3] + r[1] + se);
up(a[1], l[3] + r[1] + de);
up(a[2], l[3] + r[2] + de);
up(a[4], l[3] + r[3] + se);
up(a[3], l[3] + r[3] + de);
up(a[4], l[3] + r[4] + de);
up(a[2], l[4] + r[0] + de);
up(a[4], l[4] + r[1] + de);
up(a[4], l[4] + r[3] + de);
}
void build(int k, int l, int r) {
t[k].l = l, t[k].r = r;
if (l == r) {
t[k].a[0] = d[l];
return;
}
int mid = (l + r) >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
int se = std::min(::r[mid][0], ::r[mid][1]), de = ::r[mid][0] + ::r[mid][1];
update(t[k].a, t[k << 1].a, t[k << 1 | 1].a, se, de);
}
void query(int k, int x, int y, int res[5]) {
int l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
if (x <= l && r <= y) {
memcpy(res, t[k].a, sizeof(t[k].a));
return;
}
int se = std::min(::r[mid][0], ::r[mid][1]), de = ::r[mid][0] + ::r[mid][1];
int r1[5], r2[5];
if (x <= mid) {
if (y > mid) {
query(k << 1, x, y, r1);
query(k << 1 | 1, x, y, r2);
update(res, r1, r2, se, de);
} else
query(k << 1, x, y, res);
} else
query(k << 1 | 1, x, y, res);
}
void change(int k, int x, int y) {
int l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
if (x <= l && r <= y) {
if (l == r) {
t[k].a[0] = d[l];
return;
} else {
int se = std::min(::r[mid][0], ::r[mid][1]),
de = ::r[mid][0] + ::r[mid][1];
update(t[k].a, t[k << 1].a, t[k << 1 | 1].a, se, de);
}
} else {
int se = std::min(::r[mid][0], ::r[mid][1]), de = ::r[mid][0] + ::r[mid][1];
if (x <= mid)
change(k << 1, x, y);
if (y > mid)
change(k << 1 | 1, x, y);
update(t[k].a, t[k << 1].a, t[k << 1 | 1].a, se, de);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input", "r", stdin);
#endif
scanf("%d %d", &N, &M);
for (int i = 1; i < N; i++) {
int x;
scanf("%d", &x);
r[i][0] = l[i + 1][0] = x;
}
for (int i = 1; i < N; i++) {
int x;
scanf("%d", &x);
r[i][1] = l[i + 1][1] = x;
}
for (int i = 1; i <= N; i++) {
int x;
scanf("%d", &x);
u[i] = d[i] = x;
}
build(1, 1, N);
while (M--) {
char ch[5];
scanf("%s", ch);
if (ch[0] == 'Q') {
int A[5], x, y;
scanf("%d %d", &x, &y);
query(1, x, y, A);
printf("%d\n", A[0]);
} else {
int x0, y0, x1, y1, v;
scanf("%d %d %d %d %d", &x0, &y0, &x1, &y1, &v);
x0--, x1--;
if (x0 == x1) {
if (y0 > y1)
std::swap(y0, y1);
r[y0][x0] = l[y1][x0] = v;
change(1, y0, y1);
} else {
u[y0] = d[y0] = v;
change(1, y0, y1);
}
}
}
}