【CF1844G】Tree Weights

发布时间 2023-07-21 15:57:37作者: stoorz

题目

题目链接:https://codeforces.com/contest/1844/problem/G
给定一棵 \(n\) 个点的树,每条边有一个未知的正整数边权,给出 \(d_i\) 表示点 \(i\) 到点 \(i+1\) 的距离,求出每条边的边权或判定无解。
\(n\leq 10^5,d_i\leq 10^{12}\)

思路

\(ans[i]\) 表示点 \(i\) 到跟的距离,那么 \(ans[i]+ans[i+1]-2\times ans[\text{lca}(i,i+1)]=d_i\)
考虑在模 \(2\) 意义下的题目,也就是求出每条边二进制下最低位的值,显然此时 \(2\times ans[\text{lca}(i,i+1)]\equiv 0\pmod 2\),也就有

\[ans[i+1]\equiv d_i-ans[i]\pmod 2 \]

注意到 \(ans[1]=0\),那么我们就可以扫一遍求出来。
然后对于每个 \(d_i\),就可以减去其路径上的边二进制下最低位的贡献,然后继续求每条边二进制下倒数第二位的值,以此类推。
时间复杂度 \(O(n\log (nV))\)\(V\) 是值域。注意要记忆化两个点的 LCA。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=100010,LG=18;
int n,X[N],Y[N],dep[N],g[N],fa[N][LG+1];
ll dis[N],ans[N],val[N];
vector<int> e[N];

void dfs(int x,int fat)
{
	dep[x]=dep[fat]+1; fa[x][0]=fat;
	for (int i=1;i<=LG;i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for (auto v:e[x])
		if (v!=fat) dfs(v,x);
}

int lca(int z)
{
	if (g[z]) return g[z];
	int x=z,y=z+1;
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=LG;i>=0;i--)
		if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	if (x==y) return x;
	for (int i=LG;i>=0;i--)
		if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	return g[z]=fa[x][0];
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<n;i++)
	{
		scanf("%d%d",&X[i],&Y[i]);
		e[X[i]].push_back(Y[i]); e[Y[i]].push_back(X[i]);
	}
	for (int i=1;i<n;i++)
		scanf("%lld",&dis[i]);
	dfs(1,0);
	for (int j=0;j<=57;j++)
	{
		for (int i=1;i<n;i++)
		{
			val[i+1]=(dis[i]-val[i]+2LL)&1LL;
			ans[i+1]+=(val[i+1]<<(1LL*j));
		}
		for (int i=1;i<n;i++)
			dis[i]=((dis[i]-(val[i]+val[i+1]-2LL*val[lca(i)]))>>1);
	}
	for (int i=1;i<n;i++)
		if (dis[i]!=0LL || ans[i+1]<=ans[fa[i+1][0]]) return printf("-1"),0;
	for (int i=1;i<n;i++)
		if (dep[X[i]]<dep[Y[i]]) printf("%lld\n",ans[Y[i]]-ans[fa[Y[i]][0]]);
			else printf("%lld\n",ans[X[i]]-ans[fa[X[i]][0]]);
	return 0;
}