SDOI2015 战略游戏(动态dp)

题目链接

题目大意

给定一个只有两行的网格图,动态修改边权,查询某两列之间的网格图最小生成树的权值和。
n , m ≤ 1 0 5 n,m\le 10^5 n,m105

题解

考虑如何用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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值