【CF1844G】Tree Weights
题目
题目链接: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;
}