BZOJ 3251 树上三角形:LCA【构成三角形的结论】
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3251
题意:
给你一棵树,n个节点,每个点的权值为w[i]。
接下来有m个形如(p,a,b)的操作:
(1)p == 0:
问你在从a到b的路径上,有没有三个点满足:它们的权值大小可以构成三角形。
如果有,输出'Y',否则输出'N'。
(2)p == 1:
将点a的权值w[a]改为b。
题解:
首先考虑一个结论:
因为构成三角形的充要条件是:对于三个数a < b < c,满足a + b > c。
所以不能构成三角形的条件是:对于三个数a < b < c,满足a + b <= c。
所以对于一个升序数列a[i],让a[i]的增加速度最小,则类似于斐波那契数列:1,1,2,3,5,8,13...
又因为点权范围为[1,2^31-1],所以一个不能构成三角形的数列,最多有大约45个数字。
即:对于数字个数 >= 50的数列,必定有三个数能构成三角形。
所以先找出(a,b)的最近公共祖先act,然后顺着路径最多取50个数。
然后sort一遍取出的数t[i],扫一遍有没有t[i-2]+t[i-1]>t[i]。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <algorithm> 5 #include <vector> 6 #define MAX_N 100005 7 #define MAX_K 20 8 9 using namespace std; 10 11 int n,m; 12 int w[MAX_N]; 13 int t[MAX_N]; 14 int dep[MAX_N]; 15 int par[MAX_K][MAX_N]; 16 vector<int> edge[MAX_N]; 17 18 void read() 19 { 20 scanf("%d%d",&n,&m); 21 for(int i=1;i<=n;i++) 22 { 23 scanf("%d",&w[i]); 24 } 25 int a,b; 26 for(int i=0;i<n-1;i++) 27 { 28 scanf("%d%d",&a,&b); 29 edge[a].push_back(b); 30 } 31 } 32 33 void dfs(int now,int p,int d) 34 { 35 dep[now]=d; 36 par[0][now]=p; 37 for(int i=0;i<edge[now].size();i++) 38 { 39 int temp=edge[now][i]; 40 if(temp!=p) dfs(temp,now,d+1); 41 } 42 } 43 44 void init_lca(int root) 45 { 46 dfs(root,-1,0); 47 for(int k=0;k+1<MAX_K;k++) 48 { 49 for(int i=1;i<=n;i++) 50 { 51 if(par[k][i]==-1) par[k+1][i]=-1; 52 else par[k+1][i]=par[k][par[k][i]]; 53 } 54 } 55 } 56 57 int lca(int a,int b) 58 { 59 if(dep[a]>dep[b]) swap(a,b); 60 for(int k=0;k<MAX_N && dep[a]!=dep[b];k++) 61 { 62 if(((dep[b]-dep[a])>>k)&1) 63 { 64 b=par[k][b]; 65 } 66 } 67 if(a==b) return a; 68 for(int k=MAX_K-1;k>=0;k--) 69 { 70 if(par[k][a]!=par[k][b]) 71 { 72 a=par[k][a]; 73 b=par[k][b]; 74 } 75 } 76 return par[0][a]; 77 } 78 79 bool check(int a,int b,int act) 80 { 81 int cnt=0; 82 t[cnt++]=w[act]; 83 int now=a; 84 while(now!=act) 85 { 86 t[cnt++]=w[now]; 87 now=par[0][now]; 88 if(cnt>50) break; 89 } 90 now=b; 91 while(now!=act) 92 { 93 t[cnt++]=w[now]; 94 now=par[0][now]; 95 if(cnt>50) break; 96 } 97 sort(t,t+cnt); 98 for(int i=2;i<cnt;i++) 99 { 100 if((long long)t[i-2]+t[i-1]>t[i]) return true; 101 } 102 return false; 103 } 104 105 void work() 106 { 107 init_lca(1); 108 int p,a,b; 109 for(int i=0;i<m;i++) 110 { 111 scanf("%d%d%d",&p,&a,&b); 112 if(p==0) 113 { 114 int act=lca(a,b); 115 if(check(a,b,act)) printf("Y\n"); 116 else printf("N\n"); 117 } 118 else w[a]=b; 119 } 120 } 121 122 int main() 123 { 124 read(); 125 work(); 126 }