Problem F: tmk找三角
Description
有一棵树,树上有只tmk。他在这棵树上生活了很久,对他的构造了如指掌。所以他在树上从来都是走最短路,不会绕路。他还还特别喜欢三角形,所以当他在树上爬来爬去的时候总会在想,如果把刚才爬过的那几根树枝/树干锯下来,能不能从中选三根出来拼成一个三角形呢?
Input
第一行输入一个T,表示有多少组样例。
对于每组数据:第一行包含一个整数 N,表示树上节点的个数(从 1 到 N 标号)。
接下来的 N-1 行包含三个整数 a, b, len,表示有一根长度为 len 的树枝/树干在节点 a 和节点 b 之间。
接下来一行包含一个整数 M,表示询问数。
接下来M行每行两个整数 S, T,表示毛毛虫从 S 爬行到了 T,询问这段路程中的树枝/树干是否能拼成三角形。
Output
对于每组数据,每个询问输出一行,包含"Yes"或“No”,表示是否可以拼成三角形。
Sample Input
Sample Output
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
****************************************************************/