BZOJ5100 [POI2018]Plan metra

187 篇文章 0 订阅
33 篇文章 0 订阅

标签:构造

题目

题目传送门

Description

有一棵n个点的无根树,每条边有一个正整数权值,表示长度,定义两点距离为在树上的最短路径的长度。

已知2到 n1 n − 1 每个点在树上与1和n的距离,请根据这些信息还原出这棵树。

Input

第一行包含一个正整数 n(2n500000) n ( 2 ≤ n ≤ 500000 ) ,表示点数。

第二行包含 n2 n − 2 个正整数 d(1,2),d(1,3),...,d(1,n1) d ( 1 , 2 ) , d ( 1 , 3 ) , . . . , d ( 1 , n − 1 ) ,分别表示每个点到1的距离。

第三行包含 n2 n − 2 个正整数 d(n,2),d(n,3),...,d(n,n1) d ( n , 2 ) , d ( n , 3 ) , . . . , d ( n , n − 1 ) ,分别表示每个点到n的距离。

输入数据保证 1d1000000 1 ≤ d ≤ 1000000

Output

若无解,输出NIE。

否则第一行输出TAK,接下来n-1行每行三个正整数

u,v,c(1u,vn,1c1000000) 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

分析

首先考虑1号点和n号点存在连边的情况,直接判断其他点离1,n两点的距离进行构造

如果1号点和n号点不存在直接连边,那么我们又要分外两部分考虑

  1. 该点位于1号点和n号点之间的路径上,此时需要满足这条链上任意两个点位置不同,我们可以求出1号点和n号点之间的距离 D=min(dis(1,i)+dis(i,n)) D = min ( d i s ( 1 , i ) + d i s ( i , n ) )
  2. 对于其他点,一定是先连接到这条路径上的某一个点,然后通过这个点再连向两端的1/n号点,此时需要满足条件 dis(1,x)+dis(x,n)D d i s ( 1 , x ) + d i s ( x , n ) − D 为偶数且 abs(dis(1,x)dis(x,n))d a b s ( d i s ( 1 , x ) − d i s ( x , n ) ) ≤ d ,且存在这条路径上的某个点到1/n号点的距离为 dis(1/n,x)dis(1/n,x)+dis(n/1,x)D2 d i s ( 1 / n , x ) − d i s ( 1 / n , x ) + d i s ( n / 1 , x ) − D 2

满足这些条件的时候,可以构造出解:这条路径上的相邻两点连边,其余的点向路径上的点连边,查找对应点时可以用二分查找

如果上述的两种大情况都不满足,那么就无解

时间复杂度 O(nlogn) O ( n log ⁡ n )

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(register int i=a;i<=b;i++)
#define dep(i,a,b) for(register int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#define reg(x) for(int i=last[x];i;i=e[i].next)
using namespace std;
inline ll read(){
    ll 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;
}
//**********head by yjjr**********
const int maxn=5e5+6;
struct node{int d,p;}v[maxn];
int n,t,len=1e9,val,a[maxn],b[maxn],l[maxn],tot,x[maxn],y[maxn],z[maxn],cnt;
inline bool operator < (const node &a,const node &b){return a.d<b.d;}
int main(){
    int i;
    n=read();
    if(n==2){puts("TAK\n1 2 1");return 0;}
    rep(i,2,n-1)a[i]=read();
    rep(i,2,n-1)b[i]=read(),l[i]=a[i]+b[i],len=min(len,l[i]);
    if(a[2]!=b[2]){
        val=max(a[2]-b[2],b[2]-a[2]);
        for(i=3;i<n;i++)if(a[i]-b[i]!=val&&b[i]-a[i]!=val)break;
        if(i==n){
            printf("TAK\n1 %d %d\n",n,val);
            rep(i,2,n-1)if(a[i]<b[i])printf("1 %d %d\n",i,a[i]);else printf("%d %d %d\n",n,i,b[i]);
            return 0;
        }
    }
    rep(i,2,n-1)if(l[i]==len)v[++tot]=(node){a[i],i};
    v[++tot]=(node){len,n};v[++tot]=(node){0,1};
    sort(v+1,v+1+tot);
    rep(i,1,tot-1){
        if(v[i].d==v[i+1].d){puts("NIE");return 0;}
        x[++cnt]=v[i].p,y[cnt]=v[i+1].p,z[cnt]=v[i+1].d-v[i].d;
    }
    rep(i,2,n-1)
        if(l[i]!=len){
            if((l[i]-len)&1||(t=lower_bound(v+1,v+1+tot,(node){a[i]-((l[i]-len)>>1),0})-v)>tot||
            v[t].d!=a[i]-((l[i]-len)>>1)){puts("NIE");return 0;}
            x[++cnt]=v[t].p,y[cnt]=i,z[cnt]=(l[i]-len)>>1;
        }
    puts("TAK");
    rep(i,1,cnt)printf("%d %d %d\n",x[i],y[i],z[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值