[BZOJ 4712] 洪水(动态 DP + 树链剖分) | 错题本

文章目录

题目

[洛谷 P6021] 洪水

分析

f u f_u fu u u u 子树的答案, v v v u u u 的儿子,则 f u = max ⁡ { ∑ u f v , a u } f_u = \max\left\{\sum_{u}f_v, a_u\right\} fu=max{ufv,au} 接下来的过程与 【模板】“动态 DP“&动态树分治 几乎一致,找到转移方程的矩阵形式: ( g u a u − ∞ 0 ) × ( f h 0 ) = ( f u 0 ) \begin{pmatrix}g_u & a_u \\ -\infty & 0\end{pmatrix} \times \begin{pmatrix}f_h \\ 0\end{pmatrix} = \begin{pmatrix}f_u \\ 0\end{pmatrix} (guau0)×(fh0)=(fu0) 其中 h h h u u u 的重儿子, g u = ∑ v ≠ h f v g_u = \sum_{v \neq h} f_v gu=v=hfv。乘法定义同样是 + → max ⁡ , × → + + \to \max, \times \to + +max,×+

值得一提的是,转移方程似乎可以写成 ( g u a u ) × ( f h ) = ( f u ) \begin{pmatrix}g_u & a_u\end{pmatrix} \times \begin{pmatrix}f_h\end{pmatrix} = \begin{pmatrix}f_u\end{pmatrix} (guau)×(fh)=(fu) 但是这样虽然意义上是合法的,但转移矩阵 ( g u a u ) \begin{pmatrix}g_u & a_u\end{pmatrix} (guau) 不是一个 n × n n \times n n×n 的矩阵,所以无法用线段树维护。

代码

好像需要氧气

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>

typedef long long LL;

const int MAXN = 200000;
const LL INF = 1ll << 60;

struct Matrix {
	LL Mat[2][2];

	Matrix() {
		for (int i = 0; i < 2; i++)
			for (int j = 0; j < 2; j++)
				Mat[i][j] = INF;
	}

	LL* operator [] (const int &i) {
		return Mat[i];
	}

	Matrix operator * (Matrix other) {
		Matrix ret;
		for (int i = 0; i < 2; i++)
			for (int j = 0; j < 2; j++)
				for (int k = 0; k < 2; k++)
					ret[i][j] = std::min(ret[i][j], Mat[i][k] + other[k][j]);
		return ret;
	}

	void Debug() {
		puts("");
		for (int i = 0; i < 2; i++) {
			printf("[");
			for (int j = 0; j < 2; j++) {
				if (Mat[i][j] >= INF)
					printf("INF");
				else
					printf("%lld", Mat[i][j]);
				if (j != 1)
					putchar('\t');
			}
			printf("]\n");
		}
		puts("");
	}
}H[MAXN + 5];

int N, Q;
int A[MAXN + 5];
std::vector<int> G[MAXN + 5];

int Fa[MAXN + 5], Size[MAXN + 5], Son[MAXN + 5];
int Top[MAXN + 5], Tid[MAXN + 5], Dfn[MAXN + 5], End[MAXN + 5], DfnCnt;

struct SegmentTree {
	#define lch (u << 1)
	#define rch (u << 1 | 1)

	Matrix Mat[(MAXN << 2) + 5];

	void PushUp(int u) {
		Mat[u] = Mat[lch] * Mat[rch];
	}

	void Build(int u, int lft, int rgt) {
		if (lft == rgt) {
			Mat[u] = H[Tid[lft]];
			// printf("Build{%d %d}", lft, rgt);
			// Mat[u].Debug();
			return;
		}
		int mid = (lft + rgt) >> 1;
		Build(lch, lft, mid);
		Build(rch, mid + 1, rgt);
		PushUp(u);
	}

	void Modify(int u, int lft, int rgt, int pos) {
		if (lft == rgt) {
			Mat[u] = H[Tid[pos]];
			return;
		}
		int mid = (lft + rgt) >> 1;
		if (pos <= mid) Modify(lch, lft, mid, pos);
		else Modify(rch, mid + 1, rgt, pos);
		PushUp(u);
	}

	Matrix Query(int u, int lft, int rgt, int l, int r) {
		if (l <= lft && rgt <= r) {
			// printf("Query{%d %d}", lft, rgt);
			// Mat[u].Debug();
			return Mat[u];
		}
		int mid = (lft + rgt) >> 1;
		if (l > mid) return Query(rch, mid + 1, rgt, l, r);
		if (r <= mid) return Query(lch, lft, mid, l, r);
		return Query(lch, lft, mid, l, r) * Query(rch, mid + 1, rgt, l, r);
	}

	#undef lch
	#undef rch
}T;

void Dfs(int u, int fa) {
	int Max = 0;
	Fa[u] = fa, Size[u] = 1;
	for (int i = 0; i < int(G[u].size()); i++) {
		int v = G[u][i];
		if (v != fa) {
			Dfs(v, u);
			Size[u] += Size[v];
			if (Size[v] > Max)
				Max = Size[Son[u] = v];
		}
	}
}

LL DpF[MAXN + 5], DpG[MAXN + 5];

void Dfs(int u, int fa, int top) {
	Top[Tid[Dfn[u] = ++DfnCnt] = u] = top, End[top] = Dfn[u];
	if (Son[u]) Dfs(Son[u], u, top);
	else DpG[u] = INF;
	for (int i = 0; i < int(G[u].size()); i++) {
		int v = G[u][i];
		if (v != fa && v != Son[u]) {
			Dfs(v, u, v);
			DpG[u] += DpF[v];
		}
	}
	DpF[u] = std::min(DpG[u] + DpF[Son[u]], (LL)A[u]);
	H[u][0][0] = DpG[u], H[u][0][1] = A[u];
	H[u][1][0] = INF, H[u][1][1] = 0;
	// printf("%d: %lld %lld", u, DpF[u], DpG[u]);
	// H[u].Debug();
}

void Modify(int u, int d) {
	H[u][0][1] += d;
	while (u) {
		Matrix X = T.Query(1, 1, N, Dfn[Top[u]], End[Top[u]]);
		T.Modify(1, 1, N, Dfn[u]);
		Matrix Y = T.Query(1, 1, N, Dfn[Top[u]], End[Top[u]]);
		u = Fa[Top[u]];
		H[u][0][0] += std::min(Y[0][0], Y[0][1]) - std::min(X[0][0], X[0][1]);
	}
}

LL Query(int u) {
	// printf("Query: [%d, %d]\n", Dfn[u], End[Top[u]]);
	Matrix Ans = T.Query(1, 1, N, Dfn[u], End[Top[u]]);
	// Ans.Debug();
	return std::min(Ans[0][0], Ans[0][1]);
}

int main() {
	scanf("%d", &N);
	for (int i = 1; i <= N; i++)
		scanf("%d", &A[i]);
	for (int i = 1; i < N; i++) {
		int u, v; scanf("%d%d", &u, &v);
		G[u].push_back(v), G[v].push_back(u);
	}
	Dfs(1, 0);
	Dfs(1, 0, 1);
	T.Build(1, 1, N);
	// for (int i = 1; i <= N; i++)
		// printf("Dfn[%d] = %d, Tid[%d] = %d\n", i, Dfn[i], i, Tid[i]);
	scanf("%d", &Q);
	while (Q--) {
		char opt[3]; scanf("%s", opt);
		if (opt[0] == 'Q') {
			int u; scanf("%d", &u);
			printf("%lld\n", Query(u));
		}
		else {
			int u, d; scanf("%d%d", &u, &d);
			Modify(u, d);
		}
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值