CF1844G Tree Weights

给你一颗边权未知的树和一个数组 f,表示对于每个 i,点 ii+1 的距离。

你要尝试还原树的边权。


钦定点 1 为根,我们考虑树上两个点的距离公式

dist(x,y)=dx+dy2dlca(x,y)

di 表示点 i 的深度

然后我们记 pi=lca(i,i+1),那么不难得到

di+1=fidi+2dpi

到了这里我们直接高斯消元就可以 O(n3) 做了。

考虑是什么阻止了我们直接递推求值,原因就是不一定 pii

这样我们无法在计算 di+1 之前得到 dpi 的值。

然后这道题最神奇的地方就来了,我们发现转移式里面 dpi 带有一个 2 的系数。

这意味着我们只需要 dpi 在二进制下前 k 位的值就可以递推得到 di+1k+1 位的值。

因此我们可以先确定所有 dimod2 的值,然后就可以确定所有 dimod4 的值,依次倍增这道题就做完了。

#include <algorithm>
#include <iostream>
#include <string.h>
#include <vector>
using std::cin, std::cout;
const int N = 2e5 + 7;
typedef long long i64;

int dt[N], fa[N];
std::vector<std::pair<int, int>> g[N];

class LCAcalculator {
private:
	static const int M = std::__lg(N) + 1;
	int dp[M][N], *eu = dp[0], ti[N], de[N], id, lg[N];
	void dfs(int u=1, int f=0) {
		eu[ti[u] = ++id] = u, fa[u] = f, de[u] = de[f] + 1;
		for(auto& [v, z]: g[u]) if(v != f) {
			dfs(v, u), dt[v] = z, eu[++id] = u;
		}
	}
	inline int _min(int x, int y) {
		return de[x] < de[y] ? x : y;
	}
public:
	void init() {
		dfs(1, 0);
		for(int j = 1; j < M; ++j) {
			int t = (1 << (j - 1));
			for(int i = (1 << j); i <= id; ++i) {
				dp[j][i] = _min(dp[j-1][i], dp[j-1][i-t]);
			}
		}
		lg[1] = 0;
		for(int i = 2; i <= id; ++i)
			lg[i] = lg[i >> 1] + 1;
	}
	inline int query(int x, int y) {
		x = ti[x], y = ti[y]; if(x > y) std::swap(x, y);
		int k = lg[y - x + 1];
		return _min(dp[k][y], dp[k][x + (1 << k) - 1]);
	}
} __lca__;

int n, lca[N];
i64 ans[N], dist[N], deep[N];
int main() {
	cin >> n;
	for(int i = 1, x, y; i < n; ++i)
		cin >> x >> y, g[x].emplace_back(y, i), g[y].emplace_back(x, i);
	__lca__.init();
	for(int i = 1; i < n; ++i)
		lca[i] = __lca__.query(i, i + 1);
	for(int i = 1; i < n; ++i) cin >> dist[i];
	for(int k = 0; k < 61; ++k) {
		i64 mask = (1ll << k) - 1;
		for(int i = 1; i < n; ++i) {
			i64 vl = (dist[i] & mask) + (deep[lca[i]] << 1);
			deep[i + 1] = (vl + mask + 1 - deep[i]) & mask;
		}
	}
	for(int i = 2; i <= n; ++i) {
		if(deep[i] <= deep[fa[i]])
			cout << "-1\n", exit(0);
		ans[dt[i]] = deep[i] - deep[fa[i]];
	}
	for(int i = 1; i < n; ++i)
		if(deep[i] + deep[i + 1] - 2 * deep[lca[i]] != dist[i])
			cout << "-1\n", exit(0);
	for(int i = 1; i < n; ++i)
		cout << ans[i] << "\n";
}
posted @   CuteNess  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示