[bzoj5100][构造]Plan metra

本文介绍了一种基于两点间距离信息还原无根树结构的算法。通过分析节点1至n的链路上各点的距离,该算法能有效地重建出树的原始形态。特别地,文章详细解释了如何处理特殊情况,并提供了一段实现此算法的C++代码。

Description

有一棵n个点的无根树,每条边有一个正整数权值,表示长度,定义两点距离为在树上的最短路径的长度。
已知2到n-1每个点在树上与1和n的距离,请根据这些信息还原出这棵树。

Input

第一行包含一个正整数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。

Output

若无解,输出NIE。 否则第一行输出TAK,接下来n-1行每行三个正整数u,v,c(1<=u,v<=n,1<=c<=1000000)
表示存在一条长度为c的连接u和v两点的树边。 若有多组解,输出任意一组。

Sample Input

7

6 6 2 2 1

5 3 5 1 4

Sample Output

TAK

1 5 2

5 7 1

5 2 4

7 3 3

1 4 2

1 6 1

题解

要拍的qq滴滴我呀
你就找到树上1~n的那条链
然后在这条链上插点…
特判的话
看我代码!

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(int x){write(x);printf(" ");}
struct Nod{int d1,d2,dd,op;}w[510000];int n;
struct edge
{
	int u,v,c;
	edge(){}
	edge(int _u,int _v,int _c){u=_u;v=_v;c=_c;}
}e[510000];int len;
bool cmp(Nod n1,Nod n2){return n1.d1<n2.d1;}
bool cmp1(Nod n1,Nod n2){return (n1.d1-n1.dd)<(n2.d1-n2.dd);}
bool v[510000];
int sta[510000],tp,dis[510000];
int main()
{
// freopen("a.in","r",stdin);
//	freopen("5100.out","w",stdout);
	n=read();
	if(n==2){puts("TAK\n1 2 1\n");return 0;}
	for(int i=1;i<n-1;i++)w[i].d1=read();
	for(int i=1;i<n-1;i++)w[i].d2=read(),w[i].op=i+1;
	if(w[1].d1!=w[1].d2)
	{
		int val=abs(w[1].d1-w[1].d2);int i;
		for(i=2;i<n-1;i++)
			if(abs(w[i].d1-w[i].d2)!=val)break;
		if(i==n-1) 
		{
			puts("TAK");
			printf("1 %d %d\n",n,val);
			for(i=1;i<n-1;i++)
			{
				if(w[i].d1<w[i].d2)printf("1 %d %d\n",w[i].op,w[i].d1);
				else printf("%d %d %d\n",n,w[i].op,w[i].d2);
			}
			return 0;
		}
	}
	int u=999999999;
	sort(w+1,w+1+n-2,cmp);
	for(int i=1;i<n-1;i++)u=min(w[i].d1+w[i].d2,u);
	memset(v,false,sizeof(v));v[1]=v[n]=true;
	w[n-1].d1=0;w[n-1].d2=u;w[n-1].op=1;
	w[n].d1=u;w[n].d2=0;w[n].op=n;
	int last=n-1;sta[tp=1]=1;dis[1]=0;
	for(int i=1;i<n-1;i++)
		if(w[i].d1+w[i].d2==u)v[w[i].op]=true,e[++len]=edge(w[last].op,w[i].op,w[i].d1-w[last].d1),last=i,sta[++tp]=w[i].op,dis[tp]=w[i].d1;
	e[++len]=edge(w[last].op,w[n].op,u-w[last].d1);
	sta[++tp]=n;dis[tp]=u;
	for(int i=2;i<=tp;i++)if(dis[i]==dis[i-1]){puts("NIE");return 0;}
	for(int i=1;i<n-1;i++)
	{
		if((w[i].d1+w[i].d2-u)%2){puts("NIE");return 0;}
		w[i].dd=(w[i].d1+w[i].d2-u)/2;
	}
	sort(w+1,w+1+n-2,cmp1);
	int j=1;
	for(int i=1;i<n-1;i++)if(!v[w[i].op])
	{
		while(j<tp&&dis[j]<w[i].d1-w[i].dd)
			j++;
		if(dis[j]==w[i].d1-w[i].dd)
		{
			e[++len]=edge(w[i].op,sta[j],w[i].dd),v[w[i].op]=true;
			//printf("YES %d %d %d\n",j,w[i].op,w[i].d1-w[i].dd);
		}
		//else printf("NO %d %d %d\n",j,w[i].op,w[i].d1-w[i].dd);
	}
	if(len!=n-1)puts("NIE");
	else 
	{
		puts("TAK");
		for(int i=1;i<=len;i++)printf("%d %d %d\n",e[i].u,e[i].v,e[i].c);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值