BZOJ-3251 树上三角形
给定一大小为n的有点权树,每次询问一对点(u,v),问是否能在u到v的简单路径上取三个点权,以这三个权值为边
长构成一个三角形。同时还支持单点修改。
第一行两个整数n、q表示树的点数和操作数
第二行n个整数表示n个点的点权
以下n-1行,每行2个整数a、b,表示a是b的父亲(以1为根的情况下)
以下q行,每行3个整数t、a、b
若t=0,则询问(a,b)
若t=1,则将点a的点权修改为b
n,q<=100000,点权范围[1,2^31-1]
Output
对每个询问输出一行表示答案,“Y”表示有解,“N”表示无解。
Sample Input5 5
1 2 3 4 5
1 2
2 3
3 4
1 5
0 1 3
0 4 5
1 1 4
0 2 5
0 2 3
Sample Output
N
Y
Y
N
分析
如果三个点权要构成三角形,那么需要x+y>z。类似于斐波那契数列,可以发现,在int范围里,fibonacci数只有50个左右。那么只要简单路径的节点数超过50,必然能构成三角形。而低于50个的话就暴力遍历,排序,检查是否存在s[i]+s[i+1]>s[i+2]。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<algorithm> #include<cstring> #include <queue> #include <vector> #include<bitset> #include<map> #include<deque> using namespace std; typedef long long LL; const int maxn = 5e4+5; const int mod = 77200211+233; typedef pair<int,int> pii; #define X first #define Y second #define pb push_back //#define mp make_pair #define ms(a,b) memset(a,b,sizeof(a)) const int inf = 0x3f3f3f3f; #define lson l,m,2*rt #define rson m+1,r,2*rt+1 typedef long long ll; #define N 100010 struct use{ int st,en; }e[N*2]; int cnt,point[N],Next[N*2],top,s[N],v[N],n,x,y,q,kind,deep[N],fa[N]; void add(int x,int y){ Next[++cnt]=point[x]; point[x]=cnt; e[cnt].st=x; e[cnt].en=y; } void dfs(int x){ for (int i=point[x];i;i=Next[i]) if (e[i].en!=fa[x]){ fa[e[i].en]=x; deep[e[i].en]=deep[x]+1; dfs(e[i].en); } } void solve(int x,int y){ int t=0; while (x!=y&&t<50){ if (deep[x]>deep[y]) s[++t]=v[x],x=fa[x]; else s[++t]=v[y],y=fa[y]; } s[++t]=v[x]; if (t>=50){ printf("Y\n"); return; } sort(s+1,s+t+1); for (int i=1;i<=t-2;i++) if((long long)s[i]+s[i+1]>s[i+2]){ printf("Y\n"); return; } printf("N\n"); } int main(){ scanf("%d%d",&n,&q); for (int i=1;i<=n;i++) scanf("%d",&v[i]); for (int i=1;i<n;i++){ scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(1); for (int i=1;i<=q;i++){ scanf("%d%d%d",&kind,&x,&y); if (kind==1){ v[x]=y; continue; } solve(x,y); } return 0; }