题目大意
给定一个只有两行的网格图,动态修改边权,查询某两列之间的网格图最小生成树的权值和。
n
,
m
≤
1
0
5
n,m\le 10^5
n,m≤105
题解
考虑如何用dp求出整个网格图的最小生成树。
f
[
i
]
[
0
/
1
]
f[i][0/1]
f[i][0/1]分别表示dp到第
i
i
i列,两行末尾同属一个连通块/分属两个连通块。那么显然可以转移:
0到0,任意连接新加入的3条边其中两条。
0到1,连接横着的两条其中一条。
1到0,把三条全连上。
1到1,连接横着的两条。
于是可以写出一个转移矩阵,直接线段树维护动态dp即可。
复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
#include <bits/stdc++.h>
using namespace std;
const int MAXR = 10000000;
char _READ_[MAXR], _PRINT_[MAXR];
int _READ_POS_, _PRINT_POS_, _READ_LEN_;
inline char readc() {
#ifndef ONLINE_JUDGE
return getchar();
#endif
if (!_READ_POS_) _READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
char c = _READ_[_READ_POS_++];
if (_READ_POS_ == MAXR) _READ_POS_ = 0;
if (_READ_POS_ > _READ_LEN_) return 0;
return c;
}
template<typename T> inline void read(T &x) {
x = 0; register int flag = 1, c;
while (((c = readc()) < '0' || c > '9') && c != '-');
if (c == '-') flag = -1; else x = c - '0';
while ((c = readc()) >= '0' && c <= '9') x = x * 10 + c - '0';
x *= flag;
}
template<typename T1, typename ...T2> inline void read(T1 &a, T2&... x) {
read(a), read(x...);
}
inline int reads(char *s) {
register int len = 0, c;
while (isspace(c = readc()) || !c);
s[len++] = c;
while (!isspace(c = readc()) && c) s[len++] = c;
s[len] = 0;
return len;
}
inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
inline void printc(char c) {
_PRINT_[_PRINT_POS_++] = c;
if (_PRINT_POS_ == MAXR) ioflush();
}
inline void prints(char *s) {
for (int i = 0; s[i]; i++) printc(s[i]);
}
template<typename T> inline void print(T x, char c = '\n') {
if (x < 0) printc('-'), x = -x;
if (x) {
static char sta[20];
register int tp = 0;
for (; x; x /= 10) sta[tp++] = x % 10 + '0';
while (tp > 0) printc(sta[--tp]);
} else printc('0');
printc(c);
}
template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
print(x, ' '), print(y...);
}
typedef long long ll;
const int MAXN = 140000, INF = 0x3f3f3f3f;
struct Matrix {
int a[2][2];
Matrix operator*(const Matrix &m) const {
Matrix res;
res.a[0][0] = min(a[0][0] + m.a[0][0], a[0][1] + m.a[1][0]);
res.a[0][1] = min(a[0][0] + m.a[0][1], a[0][1] + m.a[1][1]);
res.a[1][0] = min(a[1][0] + m.a[0][0], a[1][1] + m.a[1][0]);
res.a[1][1] = min(a[1][0] + m.a[0][1], a[1][1] + m.a[1][1]);
return res;
}
} tr[MAXN];
int ls[MAXN], rs[MAXN], tag[MAXN];
int a[2][MAXN], b[MAXN], n, m, tot;
void get_mat(int k, int x) {
tr[k].a[0][0] = min(a[0][x] + a[1][x], min(a[0][x], a[1][x]) + b[x]);
tr[k].a[0][1] = min(a[0][x], a[1][x]);
tr[k].a[1][0] = a[0][x] + a[1][x] + b[x];
tr[k].a[1][1] = a[0][x] + a[1][x];
}
int build(int l, int r) {
int p = ++tot;
if (l == r) { get_mat(p, l); return p; }
int mid = (l + r) >> 1;
ls[p] = build(l, mid);
rs[p] = build(mid + 1, r);
tr[p] = tr[ls[p]] * tr[rs[p]];
return p;
}
void update(int x, int l = 1, int r = n, int k = 1) {
if (l == r) { get_mat(k, x); return; }
int mid = (l + r) >> 1;
if (x <= mid) update(x, l, mid, ls[k]);
else update(x, mid + 1, r, rs[k]);
tr[k] = tr[ls[k]] * tr[rs[k]];
}
Matrix query(int a, int b, int l = 1, int r = n, int k = 1) {
if (a <= l && b >= r) return tr[k];
int mid = (l + r) >> 1;
if (a > mid) return query(a, b, mid + 1, r, rs[k]);
if (b <= mid) return query(a, b, l, mid, ls[k]);
return query(a, b, l, mid, ls[k]) * query(a, b, mid + 1, r, rs[k]);
}
char opt[5];
int main() {
read(n, m);
for (int i = 1; i < n; i++) read(a[0][i + 1]);
for (int i = 1; i < n; i++) read(a[1][i + 1]);
for (int i = 1; i <= n; i++) read(b[i]);
build(1, n);
while (m--) {
int x0, y0, x1, y1, w;
reads(opt); read(x0, y0);
if (opt[0] == 'Q') {
if (x0 > y0) swap(x0, y0);
if (x0 == y0) { print(b[x0]); continue; }
Matrix t = query(x0 + 1, y0);
print(min(t.a[0][0] + b[x0], t.a[1][0]));
} else {
read(x1, y1, w);
if (y0 > y1) swap(y0, y1);
if (x0 == x1) a[x0 - 1][y1] = w;
else b[y1] = w;
update(y1);
}
}
ioflush();
return 0;
}