CF1844G Tree Weights

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

你要尝试还原树的边权。


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

\[dist(x,y) = d_x + d_y - 2d_{lca(x,y)} \]

\(d_i\) 表示点 \(i\) 的深度

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

\[d_{i+1} = f_i - d_i + 2d_{p_i} \]

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

考虑是什么阻止了我们直接递推求值,原因就是不一定 \(p_i \le i\)

这样我们无法在计算 \(d_{i+1}\) 之前得到 \(d_{p_i}\) 的值。

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

这意味着我们只需要 \(d_{p_i}\) 在二进制下前 \(k\) 位的值就可以递推得到 \(d_{i+1}\)\(k+1\) 位的值。

因此我们可以先确定所有 \(d_i \bmod 2\) 的值,然后就可以确定所有 \(d_i \bmod 4\) 的值,依次倍增这道题就做完了。

#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 @ 2025-02-17 11:38  CuteNess  阅读(11)  评论(0)    收藏  举报