题意
有一棵n个点的无根树,每条边有一个正整数权值,表示长度,定义两点距离为在树上的最短路径的长度。
已知2到n-1每个点在树上与1和n的距离,请根据这些信息还原出这棵树。
2<=n<=500000
分析
先把1到n路径上的节点找出来。
这里分两种情况,一种是1到n有连边。这种情况特判一下就好。
另一种是1到n没有连边,那么就找出其路径上的点,然后对于其他每个点,很容易找到它可以挂在链上的哪个节点下。
然后就做完了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int N=500005;
const int inf=1000000000;
int n,d1[N],d2[N],cnt,a[N];
bool vis[N];
struct data{int x,y,w;}ans[N];
bool cmp(int x,int y)
{
return d1[x]<d1[y];
}
bool check(int len)
{
if (!len) return 0;
for (int i=2;i<n;i++)
if (abs(d1[i]-d2[i])!=len) return 0;
puts("TAK");
printf("%d %d %d\n",1,n,len);
for (int i=2;i<n;i++)
if (d1[i]<d2[i]) printf("%d %d %d\n",1,i,d1[i]);
else printf("%d %d %d\n",i,n,d2[i]);
return 1;
}
int main()
{
scanf("%d",&n);
if (n==2) {printf("TAK\n1 2 1");return 0;}
for (int i=2;i<n;i++) scanf("%d",&d1[i]);
for (int i=2;i<n;i++) scanf("%d",&d2[i]);
if (check(abs(d1[2]-d2[2]))) return 0;
int mn=inf;
for (int i=2;i<n;i++)
if (d1[i]+d2[i]<mn) mn=d1[i]+d2[i];
int tot=0;a[++tot]=1;
for (int i=2;i<n;i++)
if (d1[i]+d2[i]==mn) a[++tot]=i,vis[i]=1;
std::sort(a+1,a+tot+1,cmp);
for (int i=1;i<tot;i++)
if (d1[a[i]]==d1[a[i+1]]) {puts("NIE");return 0;}
a[++tot]=n;
d1[n]=d1[a[2]]+d2[a[2]];
for (int i=2;i<n;i++)
{
if (vis[i]) continue;
if ((d1[i]+d2[i]-d1[n])%2==1) {puts("NIE");return 0;}
int dis=(d1[i]+d2[i]-d1[n])/2;
int l=1,r=tot,p=0;
while (l<=r)
{
int mid=(l+r)/2;
if (d1[i]-dis==d1[a[mid]]) {p=mid;break;}
if (d1[i]-dis<d1[a[mid]]) r=mid-1;
else l=mid+1;
}
if (!p) {puts("NIE");return 0;}
ans[++cnt]=(data){i,a[p],dis};
}
for (int i=1;i<tot;i++) ans[++cnt]=(data){a[i],a[i+1],d1[a[i+1]]-d1[a[i]]};
puts("TAK");
for (int i=1;i<=cnt;i++) printf("%d %d %d\n",ans[i].x,ans[i].y,ans[i].w);
return 0;
}