题目大意:给你一棵树,问你树中两点之间的最短路上的边集合中是否可以找到三条边,使得它们可以组成一个三角形。
节点数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; }