妙妙题集合¶
妙妙题指的是:
-
诈骗题
-
依赖“数字范围”的题(注意不是数据范围)
-
不依赖具体算法(得出结论后暴力可过)
#1 树上三角形¶
定一大小为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]\)
输出格式¶
对每个询问输出一行表示答案,“Y”表示有解,“N”表示无解。
注意到一个集合如果没有一组合法的,那么这个集合最差(即最大)的构造为一个斐波那契数列。又发现点权范围内的斐波那契数列的长度<50,因此断定:长度超过50的链必定合法。
// Problem: E. 树上三角形
// Contest: LibreOJ - CSP2024专题复习1
// URL: http://www.nfls.com.cn:20035/contest/2072/problem/5
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// Challenger: Erica N
// ----
#include<bits/stdc++.h>
using namespace std;
#define rd read()
#define ull unsigned long long
#define int long long
#define itn int
#define ps second
#define pf first
int read(){
int x;
cin>>x;
return x;
}
#define zerol = 1
#ifdef zerol
#define cdbg(x...) do { cerr << #x << " -> "; err(x); } while (0)
void err() {
cerr << endl;
}
template<template<typename...> class T, typename t, typename... A>
void err(T<t> a, A... x) {
for (auto v: a) cerr << v << ' ';
err(x...);
}
template<typename T, typename... A>
void err(T a, A... x) {
cerr << a << ' ';
err(x...);
}
#else
#define dbg(...)
#endif
const int N=3e5+5;
const ull P=137;
const int INF=1e9+7;
/*
策略
可以得到三角形的条件是什么?存在权a+b>c且a<=b<=c
c-a-b<0
太巧妙了!!
*/
#define pb push_back
int stk[N],top;
vector<int> e[N];
void add(int a,int b){
e[a].pb(b);
e[b].pb(a);
}
namespace LCA{
int dep[N];int fa[N][22];
void dfs(int x,int f){
dep[x]=dep[f]+1;
for(auto v:e[x]){
if(v==f)continue;
fa[v][0]=x;
for(int i=1;i<=20;i++){
fa[v][i]=fa[fa[v][i-1]][i-1];
}
dfs(v,x);
}
}
int lca(int a,int b){
if(dep[a]<dep[b])a^=b^=a^=b;
for(int i=10;~i;i--){
if(dep[fa[a][i]]>=dep[b])a=fa[a][i];
}
if(a==b)return a;
for(int i=20;~i;i--){
if(fa[a][i]!=fa[b][i]){
a=fa[a][i];
b=fa[b][i];
}
}
return fa[a][0];
}
int anc;
int getDis(int a,int b){
anc=lca(a,b);
return dep[a]-dep[anc]+dep[b]-dep[anc]+1;
}
}using namespace LCA;
int w[N];
bool query(int a,int b){
if(getDis(a,b)>50)return 1;
cdbg(a,b);
top=0;
while(a!=anc){
stk[++top]=w[a];
a=fa[a][0];
}
while(b!=anc){
stk[++top]=w[b];
b=fa[b][0];
}
stk[++top]=w[anc];
sort(stk+1,stk+top+1);
for(int i=1;i+2<=top;i++){
// cdbg(stk[i],stk[i+1],stk[i+2]);
if(stk[i]+stk[i+1]>stk[i+2])return 1;
}
return 0;
}
signed main(){
int n=rd,q=rd;
for(int i=1;i<=n;i++){
w[i]=rd;
}
for(int i=1;i<n;i++){
add(rd,rd);
}
dfs(1,0);
while(q--){
int op=rd,a=rd,b=rd;
if(op){
w[a]=b;
}else{
if(query(a,b))puts("Y");
else puts("N");
}
}
}