GDUTOJ 1229 tmk找三角

Problem F: tmk找三角

Description

有一棵树,树上有只tmk。他在这棵树上生活了很久,对他的构造了如指掌。所以他在树上从来都是走最短路,不会绕路。他还还特别喜欢三角形,所以当他在树上爬来爬去的时候总会在想,如果把刚才爬过的那几根树枝/树干锯下来,能不能从中选三根出来拼成一个三角形呢?

Input

第一行输入一个T,表示有多少组样例。

对于每组数据:第一行包含一个整数 N,表示树上节点的个数(从 到 标号)。

接下来的 N-1 行包含三个整数 a, b, len,表示有一根长度为 len 的树枝/树干在节点 和节点 之间。

接下来一行包含一个整数 M,表示询问数。

接下来M行每行两个整数 S, T,表示毛毛虫从 爬行到了 T,询问这段路程中的树枝/树干是否能拼成三角形。

Output

对于每组数据,每个询问输出一行,包含"Yes"“No”,表示是否可以拼成三角形。

Sample Input

2
5
1 2 5
1 3 20
2 4 30
4 5 15
2
3 4
3 5
5
1 4 32
2 3 100
3 5 45
4 5 60
2
1 4
1 3

Sample Output

No
Yes
No
Yes

HINT


对于20%数据 1 ≤ N, M ≤ 1000


对于所有数据 1 ≤ N ≤ 100000, 1 ≤ M ≤ 100000, 1 ≤ len ≤ 1000000000


提示

题意:

略。

思路:

先给出官方(貌似是)题解:

假设现在有 n 条线段,假设 n 条边从小到达排序,如果这 n 条边中没有三条可以构成 三角形,那么这 n 条边必须满足关系:A[i]>=A[i-2]+A[i-1],这里的 A[i]表示第 i 条边的大小。 假设 A[i]尽量取最小 A[i]=A[i-2]+A[i-1],且 A[1]=A[2]=1,是不是就是一个斐波那契,也就 是对于一个 n 条边的集合,如果不存在三条边能构成一个三角形,那么最长的边至少为 f[n], 表示斐波那契第 n 项。而题目中 A[i]<1e9,也就是只要 n>50,就必定存在三条边可以构成一 个三角形,所以我们只需要暴力加入两点路径上的边(如果大于 50,直接 Yes),然后对这 些边进行排序,枚举第 i 条边为最长边,贪心判断 A[i]是否小于 A[i-1]+A[i-2]即可。

简单来说就是路径数大于50强行Yes,否则进行判断,把需要判断的次数降了下来。

最重要的一点是需要先利用dfs进行预处理对所有点扫一遍把路径存下来,不过要怎么存呢?

我们可以根据题目的n-1条边这一条件,利用LCA的思想以节点1为根把每个点的前驱,相对于节点1的深度(路径个数)以及长度记录下来,当然要把节点1进行初始化一下。之后比较起点和终点的深度,深度比较深的取出路径(长度相等两个任意选一个),该点更新为父亲节点。直到起点等于终点,最后进行判断即可。还是不理解的话看图(以样例第一组数据为例):


我们看s=3,t=4时:

deep[4]>deep[3],长为30的路径取出,数组长度为1,t=2;

deep[3]==deep[2],长为5的路径取出,数组长度为2(也可取长为20的路径),t=1;

deep[3]>deep[1],长为20的路径取出,数组长度为3,s=1;

这时s等于t,结束,之后进行判断。

我的程序节点编号是从0到n-1。

示例程序

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
struct jj
{
    int v,next,wi;
}w[200000];
struct kk
{
    int pre,wi,deep;
}p[100000];
int h[100000],numw;
void inser(int u,int v,int wi)
{
    w[numw].v=v;
    w[numw].wi=wi;
    w[numw].next=h[u];
    h[u]=numw;
    numw++;
}
void dfs(int t,int deep)
{
    int i;
    for(i=h[t];i!=-1;i=w[i].next)
    {
        if(w[i].v!=p[t].pre)
        {
            p[w[i].v].pre=t;			//节点信息记录
            p[w[i].v].wi=w[i].wi;
            p[w[i].v].deep=deep;
            dfs(w[i].v,deep+1);
        }
    }
}
void output(int s,int t)
{
    int i,a[51],top;
    for(top=0;50>=top&&s!=t;top++)				//路径数大于50或者s==t结束循环
    {
        if(p[s].deep>=p[t].deep)
        {
            a[top]=p[s].wi;
            s=p[s].pre;
        }
        else
        {
            a[top]=p[t].wi;
            t=p[t].pre;
        }
    }
    if(top<3)			//三边都没有怎么可能有三角形
    {
        printf("No\n");
    }
    else if(top==51)				//一定存在三角形
    {
        printf("Yes\n");
    }
    else
    {
        sort(a,a+top);
        for(i=0;top>i+2;i++)			//暴力判断一遍
        {
            if(a[i]+a[i+1]>a[i+2])
            {
                printf("Yes\n");
                return;
            }
        }
        printf("No\n");
    }
}
int main()
{
    int n,m,k,i,t,u,v,wi;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(h,-1,sizeof(h));
        numw=0;
        for(i=1;n>i;i++)
        {
            scanf("%d %d %d",&u,&v,&wi);
            inser(u-1,v-1,wi);
            inser(v-1,u-1,wi);
        }
        p[0].pre=0;
        p[0].deep=0;			//初始化根
        dfs(0,1);			//对着根跑一边dfs
        scanf("%d",&k);
        for(i=1;k>=i;i++)
        {
            scanf("%d %d",&u,&v);
            output(u-1,v-1);
        }
    }
    return 0;
}
 
/**************************************************************
    Problem: 1229
    Code Length: 2059B
    Language: C++
    Result: Accepted
    Time:76 ms
    Memory:5000 kb
****************************************************************/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值