首页 > 其他分享 >洛谷 P8820 [CSP-S 2022] 数据传输 题解

洛谷 P8820 [CSP-S 2022] 数据传输 题解

时间:2022-11-02 00:01:09浏览次数:50  
标签:infty P8820 洛谷 int 题解 top son dep lca

首先考虑对于每一次询问暴力 DP。

设 \(f_{u,i}\) 表示从 \(s\) 开始,传到一个到 \(u\) 距离为 \(i\) 的点,需要的最短时间。

当 \(k = 3\) 时,可能会传到不在 \(s \to t\) 路径上的点 \(x\)。假设从 \(a\) 点经过 \(x\) 传输到 \(b\) 点,若 \(x\) 到 \(s \to t\) 路径上最近点的距离大于等于 \(2\),则 \(dist(a,b) \le 2\),\(a\) 传到 \(b\) 没有必要经过 \(x\)。所以只需要考虑到路径上的点距离不超过 \(1\) 的点。

记 \(w_u = \begin{cases}\min \limits_{dist(u,v)\le 1} a_v &(k=3) \\ +\infty &(k \le 2)\end{cases}\)。

\(k = 3\) 时,

\(f_{u,0} = \min\{f_{v,0}, f_{v,1}, f_{v,2}\} + a_u\),

\(f_{u,1} = \min\{f_{v,0}, f_{v,1} + w_u\}\),

\(f_{u,2} = f_{v,1}\)。

时间复杂度是 \(O(nq)\)。

优化这个 DP。传输时间与方向无关,\(s \to t\) 可以拆成 \(s \to lca\) 和 \(t \to lca\),只需要求出这两段的 DP 结果再合并即可。

定义广义矩阵乘法 \(C_{i,j} = \min\limits_{0 \le p < k}\{A_{i,p}+B_{p,j}\}\)。

DP 转移矩阵:\(\begin{bmatrix} a_u & 0 & +\infty \\ a_u & w_u & 0 \\ a_u & +\infty & +\infty \end{bmatrix}\)。

初始向量:\(\begin{bmatrix} a_u & +\infty & +\infty \end{bmatrix}\)。

树链剖分,对于每一条重链预处理前缀矩阵乘积,用线段树维护区间矩阵乘积。询问时跳重链,如果当前点 \(x\) 和 \(lca\) 在同一条重链上,线段树查询。否则一定是重链的前缀,直接乘前缀积即可。注意如果两段都选了 \(lca\),结果应该减去 \(a_{lca}\)。

时间复杂度 \(O(nk^3 + qk^2 \log n)\),空间复杂度 \(O(nk^2)\),均优于用倍增维护矩乘的做法。其实时空限制 1s 256MB 也能过,但良心出题人没有卡。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 2e5 + 5;
const ll INF = 1e18;
int n, q, k;
ll a[N], w[N];
vector<int> tree[N];

int dep[N], fa[N], siz[N], son[N];

void dfs1(int u, int pre) {
	dep[u] = dep[pre] + 1;
	fa[u] = pre;
	siz[u] = 1;
	for (int v : tree[u])
		if (v != pre) {
			dfs1(v, u);
			siz[u] += siz[v];
			if (siz[v] > siz[son[u]]) son[u] = v;
		}
}

int dfn[N], bac[N], top[N], cnt;

void dfs2(int u, int tp) {
	dfn[u] = ++cnt;
	bac[cnt] = u;
	top[u] = tp;
	if (son[u]) dfs2(son[u], tp);
	for (int v : tree[u])
		if (v != fa[u] && v != son[u])
			dfs2(v, v);
}

inline int LCA(int x, int y) {
	int tx = top[x], ty = top[y];
	while (tx != ty) {
		if (dep[tx] >= dep[ty]) x = fa[tx], tx = top[x];
		else y = fa[ty], ty = top[y];
	}
	if (dep[x] <= dep[y]) return x;
	return y;
}

struct Mat {
	int n;
	ll s[3][3];
	
	Mat() {}
	Mat(int n) : n(n) {
		for (int i = 0; i < n; ++i)
			for (int j = 0; j < k; ++j)
				s[i][j] = INF;
	}
	
	void init(int u) {
		n = k;
		s[0][0] = s[1][0] = s[2][0] = a[u];
		s[0][1] = s[1][2] = 0;
		s[1][1] = k == 3 ? w[u] : INF;
		s[0][2] = s[2][1] = s[2][2] = INF;
	}
	
	void start(int u) {
		n = 1;
		s[0][0] = a[u];
		s[0][1] = s[0][2] = INF;
	}
};

inline Mat operator * (const Mat &A, const Mat &B) {
	Mat C(A.n);
	for (int i = 0; i < A.n; ++i)
		for (int p = 0; p < k; ++p)
			for (int j = 0; j < k; ++j)
				C.s[i][j] = min(C.s[i][j], A.s[i][p] + B.s[p][j]);
	return C;
}

Mat val[N], pre[N];

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

Mat prod[N << 2];

inline void pushup(int x) {
	prod[x] = prod[rs(x)] * prod[ls(x)];
}

void build(int x = 1, int l = 1, int r = n) {
	if (l == r) {
		prod[x] = val[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(rs(x), mid + 1, r);
	build(ls(x), l, mid);
	pushup(x);
}

void query(int L, int R, Mat &res, int x = 1, int l = 1, int r = n) {
	if (L <= l && r <= R) {
		res = res * prod[x];
		return;
	}
	int mid = (l + r) >> 1;
	if (R > mid) query(L, R, res, rs(x), mid + 1, r);
	if (L <= mid) query(L, R, res, ls(x), l, mid);
}

Mat res1, res2;

inline void ask(int x, int lca, Mat &res) {
	res.start(x);
	if (x == lca) return;
	x = fa[x];
	while (top[x] != top[lca]) {
		res = res * pre[dfn[x]];
		x = fa[top[x]];
	}
	query(dfn[lca], dfn[x], res);
}

inline ll solve(int lca) {
	ll ans = res1.s[0][0] + res2.s[0][0] - a[lca];
	for (int i = 0; i < k; ++i)
		for (int j = 0; j < k; ++j)
			if (i + j <= k)
				ans = min(ans, res1.s[0][i] + res2.s[0][j]);
	return ans; 
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> q >> k;
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
		w[i] = a[i];
	}
	for (int i = 1; i < n; ++i) {
		int u, v;
		cin >> u >> v;
		tree[u].push_back(v);
		tree[v].push_back(u);
		w[u] = min(w[u], a[v]);
		w[v] = min(w[v], a[u]);
	}
	dfs1(1, 0);
	dfs2(1, 1);
	for (int i = 1; i <= n; ++i)
		val[dfn[i]].init(i);
	for (int i = 1; i <= n; ++i) {
		if (bac[i] == top[bac[i]])
			pre[i] = val[i];
		else
			pre[i] = val[i] * pre[i - 1];
	}
	build();
	while (q--) {
		int s, t, lca;
		cin >> s >> t;
		lca = LCA(s, t);
		ask(s, lca, res1);
		ask(t, lca, res2);
		cout << solve(lca) << '\n';
	}
	return 0;
}

标签:infty,P8820,洛谷,int,题解,top,son,dep,lca
From: https://www.cnblogs.com/2ha-maomao-2006/p/p8820-solution.html

相关文章

  • 洛谷 P7207
    首先题目给出结论,对于任意\(n,m\)均有解。所以如果\(A\)中后\(x\)个数和\(B\)中前\(x\)个数两两配对,就可以转化为\(n-x,m+x\)的子问题。所以对于\(A\)中最......
  • 洛谷-P2015 二叉苹果树
    二叉苹果树树形dp设计状态:\(dp[u][i]\),表示以结点\(u\)为根的子树,保留\(i\)条边的最大苹果数状态转移:遍历每一个子节点\(v\)保留和\(v\)相连的边:\(dp[u][i]=......
  • spring-boot 配置 swagger常见版本不匹配问题解决方案
    https://stackoverflow.com/questions/70036953/spring-boot-2-6-0-spring-fox-3-failed-to-start-bean-documentationpluginsboo一般出现在2.6.*的springboot版本,这里解......
  • 洛谷-P1122 最大子树和
    最大子树和树形dp对于一个节点来说,如果删除掉一个连接子节点的边,则以该子节点为根的子树上面的贡献都会变成\(0\)设计状态:\(dp[u]\),表示以\(u\)为根的子树中,贡献值最......
  • [abc265F] Erase Subarrays 题解
    空心蓝好简单。为什么我还是打不上蓝呢。为什么我次次debug40+min呢/ll题意:给定数组\(a\),对每个\(i\in[1,m]\)求最少做几次操作让剩余元素和为\(i\)。一次操......
  • 洛谷 P3183 [HAOI2016]食物链(dfs+记忆化搜索)
    https://www.luogu.com.cn/problem/P3183题目大意:给定n个节点,标号分别为1——n,然后给出m条有向边,问我们不同的食物链路径有多少?输入#1101612141102325......
  • iOS上拉边界下拉白色空白问题解决概述
    表现手指按住屏幕下拉,屏幕顶部会多出一块白色区域。手指按住屏幕上拉,底部多出一块白色区域。产生原因在iOS中,手指按住屏幕上下拖动,会触发 touchmove 事件。这个事......
  • 洛谷P1144
    最短路计数题目描述给出一个\(N\)个顶点\(M\)条边的无向无权图,顶点编号为\(1\simN\)。问从顶点\(1\)开始,到其他每个点的最短路有几条。输入格式第一行包含\(2......
  • 洛谷 P6573 [BalticOI 2017] Toll 题解
    Link算是回归OI后第一道自己写的题(考CSP的时候可没回归)写篇题解纪念一下题目大意:\(n\)个点,\(m\)条单向边,每条边的两端点\(x\),\(y\)必定满足\(\left\lfloor\dfrac......
  • DJango + Vue 跨域问题解决
    什么是跨域同源:协议+域名+端口号,三者完全相同以上三个元素只要有一个不相同就是跨域产生跨域异常的报错信息如下:accesstoxmlhttprequestat'http://ip:port1/a......