题目大意:给你一棵树,问你树中两点之间的最短路上的边集合中是否可以找到三条边,使得它们可以组成一个三角形。
节点数100000,询问数100000,边长的范围是[1,1000000000]。
思路:对于这道题,直接将两点之间的边求出来再判断肯定是不现实的(小数据就可以。。。),我们不妨先来求一个简化的问题,即:给你一系列边,问你从中是否可以选择三条边组成一个三角形。对于给定三条边a,b,c,它们能组成一个三角形当且仅当
1.a+b>c
2.b+c>a
3.a+c>b同时成立。
我们发现如果a<=b<=c,则只要满足a+b>c即可组成一个三角形(其他两个条件显然),所以我们不妨先对所给的边集按照非递减排一个序,设排序后的序列为a1,a2,a3...an,现在我们则是要在序列a中找到三条可以组成三角形的边。事实上,我们可以在O(n)的时间内,求出答案。我们从a3开始遍历,求最大边是ai时,是否可以找到两条边aj,ak使得j<i&&k<i&&k!=j&&aj+ak>ai。显然如果ai-1+ai-2<=ai(这里的ai-1指的是第i-1个数),则不存在以ai为最大边的三角形,否则,ai-2,ai-1,ai即为一个可行解,所以我们只要比较连续三个数的大小即可,时间复杂度是O(nlogn)。但是,对于n很大的情况我们还是无法在有效时间内得出解。
这时注意题目的要求,它是要求是否存在解,而不关心解是什么。通过刚才的分析我们可以知道,对于无解的情况,n一定不会很大,我们可以贪心求出n的最大值。
假设我们已经排好序,序列为b1,.....bn,则对于无解的情况,序列b要满足:
1:bi+bi+1<=bi+2。
2:bi<=bi+1。
因为我要n尽可能的大,所以第一个条件可以取==,我们现在开始构造b
前两个数显然是1,1,第三个数由第一个条件为2,第4个为1+2=3,第五个为2+3=5,。。。。。我们可以发现这其实就是斐波那契数列,我们可以求出对于第45个数时,它的值第一次超过1000000000,所以对于题目给出的数据范围,n最大为44,所以这就好求了。
对于两点间边数超过44的直接输出Yes,否则就暴力将这些边求出来再有上面讲的方法求即可,最大不过44条边,所以还是很快的。求两点之间的路径用LCA即可。下面附代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define maxn 100010
using namespace std;
struct edge
{
int to;
int next;
int len;
}e[maxn<<1];
int box[maxn],cnt,tot;
int siz[maxn],top[maxn],son[maxn],dep[maxn],fa[maxn],len[maxn];
void init()
{
tot=0;
son[0]=dep[0]=0;
memset(box,-1,sizeof(box));
cnt=0;
}
void add(int from,int to,int len)
{
e[cnt].to=to;
e[cnt].len=len;
e[cnt].next=box[from];
box[from]=cnt++;
}
void dfs(int now,int pre,int le)
{
siz[now]=1;
fa[now]=pre;
len[now]=le;
son[now]=0;
dep[now]=dep[pre]+1;
int t,v,l;
for(t=box[now];t+1;t=e[t].next)
{
v=e[t].to,l=e[t].len;
if(v!=pre)
{
dfs(v,now,l);
siz[now]+=siz[v];
if(siz[son[now]]<siz[v])
{
son[now]=v;
}
}
}
}
void dfs2(int now,int tp)
{
top[now]=tp;
if(son[now])
dfs2(son[now],top[now]);
int t,v;
for(t=box[now];t+1;t=e[t].next)
{
v=e[t].to;
if(v!=fa[now]&&v!=son[now])
dfs2(v,v);
}
}
int LCA(int a, int b)
{
while (1)
{
if (top[a] == top[b])
return dep[a] <= dep[b] ? a : b;
else if (dep[top[a]] >= dep[top[b]])
a = fa[top[a]];
else b = fa[top[b]];
}
}
int aa[50];
int check(int num)
{
if(num<3)
return 0;
sort(aa,aa+num);
for(int i=0;i<num-2;i++)
{
if(aa[i]+aa[i+1]>aa[i+2])
return 1;
}
return 0;
}
void solve(int a,int b,int c)
{
int num=0;
while(a!=c)
{
aa[num++]=len[a];
a=fa[a];
}
while(b!=c)
{
aa[num++]=len[b];
b=fa[b];
}
if(check(num))
printf("Yes\n");
else
printf("No\n");
}
int main()
{
//freopen("dd.txt","r",stdin);
int ncase,time=0;
scanf("%d",&ncase);
while(ncase--)
{
printf("Case #%d:\n",++time);
init();
int n,m,i,a,b,c;
scanf("%d",&n);
for(i=1;i<n;i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
dfs(1,0,0);
dfs2(1,1);
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&a,&b);
c=LCA(a,b);
//printf("%d %d %d\n",a,b,c);
int limit=dep[a]+dep[b]-2*dep[c];
if(limit>44)
printf("Yes\n");
else
{
solve(a,b,c);
}
}
}
return 0;
}