Description
有一棵n个点的无根树,每条边有一个正整数权值,表示长度,定义两点距离为在树上的最短路径的长度。
已知2到n-1每个点在树上与1和n的距离,请根据这些信息还原出这棵树。
第一行包含一个正整数n(2<=n<=500000),表示点数。
第二行包含n-2个正整数d(1,2),d(1,3),…,d(1,n-1),分别表示每个点到1的距离。
第三行包含n-2个正整数d(n,2),d(n,3),…,d(n,n-1),分别表示每个点到n的距离。
输入数据保证1<=d<=1000000。
若无解,输出NIE。
否则第一行输出TAK,接下来n-1行每行三个正整数u,v,c(1<=u,v<=n,1<=c<=1000000)
表示存在一条长度为c的连接u和v两点的树边。
若有多组解,输出任意一组。
Solution
考虑1到n这条链,设它的长度为mn,显然
m
n
=
min
{
d
(
1
,
i
)
+
d
(
i
,
n
)
}
mn=\min\left\{d(1,i)+d(i,n)\right\}
mn=min{d(1,i)+d(i,n)}。证明的话可以考虑反证一下
那么把在这条链上的点抠出来,剩余的点我们依次判断。
对于点i,我们有
f
=
d
(
1
,
i
)
+
d
(
i
,
n
)
−
m
n
2
f=\frac{d(1,i)+d(i,n)-mn}{2}
f=2d(1,i)+d(i,n)−mn,其中f表示i到链的距离,这样我们也就知道了与i相连的点到1的距离,可以二分出这个点来
当然还存在一种情况就是1和n直接相连,那么剩余的点 ∣ d ( 1 , i ) − d ( i , n ) ∣ |d(1,i)-d(i,n)| ∣d(1,i)−d(i,n)∣全部相等,其余点要么连向1要么连向n
无解的情况比较多,要细心。。
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fi first
#define se second
typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=500055;
struct pair {
LL fi,se,id;
bool operator <(const pair &b) const {
return fi<b.fi;
}
} p[N],v[N];
std:: vector <pair> ans;
bool vis[N];
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void add_edge(LL x,LL y,LL w) {
ans.push_back((pair) {x,y,w});
}
int main(void) {
int n=read(),c=0; LL mn=1LL<<30;
if (n==2) return 0&puts("TAK\n1 2 1");
rep(i,2,n-1) p[i].fi=read();
rep(i,2,n-1) p[i].se=read();
rep(i,2,n-1) p[i].id=i;
LL lxf=abs(p[2].fi-p[2].se);
if (lxf) {
bool flag=true;
rep(i,3,n-1) {
if (abs(p[i].fi-p[i].se)!=lxf) {
flag=false;
break;
}
}
if (flag) {
puts("TAK");
printf("%d %d %lld\n", 1,n,lxf);
rep(i,2,n-1) {
if (p[i].fi<p[i].se) printf("%d %d %lld\n", 1,i,p[i].fi);
else printf("%d %d %lld\n", n,i,p[i].se);
}
return 0;
}
}
rep(i,2,n-1) mn=std:: min(mn,p[i].fi+p[i].se);
rep(i,2,n-1) if (p[i].fi+p[i].se==mn) {
v[++c]=p[i];
vis[i]=true;
}
v[++c]=(pair) {0,mn,1};
v[++c]=(pair) {mn,0,n};
std:: sort(v+1,v+c+1);
rep(i,2,c) {
if (v[i-1].fi==v[i].fi) return 0&puts("NIE");
add_edge(v[i-1].id,v[i].id,v[i].fi-v[i-1].fi);
}
rep(i,2,n-1) if (!vis[i]) {
LL tmp=p[i].fi+p[i].se-mn;
if (tmp&1) return 0&puts("NIE");
tmp=p[i].fi-tmp/2;
pair wjp=(pair) {tmp,0,0};
int it=std:: lower_bound(v+1,v+c+1,wjp)-v;
if (it>c) return 0&puts("NIE");
if (v[it].fi!=tmp) return 0&puts("NIE");
add_edge(v[it].id,i,p[i].fi-tmp);
}
puts("TAK");
for (int i=0;i<ans.size();++i) {
printf("%lld %lld %lld\n", ans[i].fi,ans[i].se,ans[i].id);
}
return 0;
}