Description
给定一大小为n的有点权树,每次询问一对点(u,v),问是否能在u到v的简单路径上取三个点权,以这三个权值为边长构成一个三角形。同时还支持单点修改。
Input
第一行两个整数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
Output
对每个询问输出一行表示答案,“Y”表示有解,“N”表示无解。
Sample Input
5 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
HINT
对于100%的数据,n,q<=100000,点权范围[1,231-1]
Source
路径上节点个数大于50直接输出Y否则暴力..
zky学长当时是这么讲的…至于为什么?
引自黄学长blog:
若路径上有两点的点权为x,y
则若有个点z且z > abs(x-y)且z < x+y,则可以构成三角形
类似斐波那契数列1 2 3 5 8 13 。。。
发现最好情况下int范围只有不到50个点满足无法构成三角形
那么只要路径点超过50个直接输出Y,否则暴力
就是这样
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
#define MAXN 100100
using namespace std;
int n,q,top,Top;
int deep[MAXN],fa[MAXN],sta[MAXN],val[MAXN];
struct edge
{
int to;
edge *next;
}e[MAXN<<1],*prev[MAXN];
void insert(int u,int v)
{
e[++Top].to=v;e[Top].next=prev[u];prev[u]=&e[Top];
}
void in(int &x)
{
char ch=getchar();x=0;int flag=1;
while (!(ch>='0'&&ch<='9')) flag=ch=='-'?-1:flag,ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();x*=flag;
}
void dfs(int x)
{
for (edge *i=prev[x];i;i=i->next) deep[i->to]=deep[x]+1,fa[i->to]=x,dfs(i->to);
}
void query(int a,int b)
{
top=0;
while (top<50&&a!=b)
{
if (deep[a]>deep[b]) sta[++top]=val[a],a=fa[a];
else sta[++top]=val[b],b=fa[b];
}
sta[++top]=val[a];
if (top>=50) {puts("Y");return;}
sort(sta+1,sta+top+1);
for (int i=1;i<top-1;i++)
if ((LL)sta[i]+sta[i+1]>sta[i+2]) {puts("Y");return;}
puts("N");
}
int main()
{
in(n);in(q);
for (int i=1;i<=n;i++) in(val[i]);
for (int i=1;i<n;i++)
{
int u,v;in(u);in(v);insert(u,v);
}
dfs(1);
for (int i=1;i<=q;i++)
{
int opt,u,v;in(opt);in(u);in(v);
if (!opt) query(u,v);
else val[u]=v;
}
}