树上三角形

题目描述
给定一个大小为n的有点权树,需要支持两个操作。
0:询问(u,v),能否在u到v的简单路径上取三个点,使这三个点的点权作为边长可以构成一个三角形。
1:修改某个点的点权。
输入格式
第一行两个整数n,q表示树的点数和操作数。
第二行n个整数表示n个点的初始的点权。
接下来n-1行,每行两个整数a,b,表示a是b的父亲。
接下来q行,每行三个整数op,a,b:
若op=0,则表示询问(a,b)。
若op=1,则表示将a的点权修改为b。
输出格式
对每个询问输出一行表示答案,“Y”表示有解,“N”表示无解,(不包括引号)
样例输入
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
样例输出
N
Y
Y
N
样例解释
对于前10%的数据,n,q<=100
对于前30%的数据,n,q<=1000
对于另外40%的数据,无修改操作
对于100%的数据,n,q<=100000,点权范围[1,2^31-1]
题目解析:

这道题正解是暴力,具体分为以下几个步骤:
1.先证明一下这道题,如果这两个点的距离超过47左右,就一定可以输出Y。
我们可以用菲薄那切数列来推,因为超过47左右,那就差不多会爆int范围,而这道题的范围是int
那为什么超过int的范围后就一定可以呢?我们看一下,这道题是要找出三个点,
把他们当作三角形的三边,然后看能不能组成一个三角形。然后我们考虑一下边界情况,当a+b=c时
一直往后推,就是菲薄那切了。然后要在int范围内,并且如果距离是大于47的,我们再把式子变形成a=c-b
那么在int范围内肯定是a>b-c的,所以是符合的(应该没说错吧)。
2.这给时候我们用树剖或者是倍增求lca,然后求出他们的距离,直接判断距离。如果距离小于47,那么
把那些点权排序,暴力判断一下就好了,只要存在就可以输出Y。都没有就输出N

/*此代码是本校的一位大佬的代码,自己的在考试中没有过,后面改了下,发现有些地方得大改一下,于是不想改了,就放这位大佬的吧*/
#include<cstdio>
#include<algorithm>
#define re register int
#define fp(i,a,b) for(re i=a,I=b;i<=I;++i)
#define fd(i,a,b) for(re i=a,I=b;i>=I;--i)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){if(A==B){B=(A=ss)+fread(ss,1,1<<17,stdin);if(A==B)return EOF;}return*A++;}
template<class T>inline void sdf(T&x){
    char c;re y=1;while(c=gc(),c<48||57<c)if(c=='-')y=-1;x=c^48;
    while(c=gc(),47<c&&c<58)x=(x<<1)+(x<<3)+(c^48);x*=y;
}
const int N=1e5+5;
typedef int arr[N];
struct edges{int nx,to;}e[N<<1];
int n,m,tot,tp;arr fi,w,stk,dep,top,son,sz,fa;
inline void add(re u,re v){e[++tot]=(edges){fi[u],v};fi[u]=tot;}
void dfs1(re u){
    dep[u]=dep[fa[u]]+(sz[u]=1);
    for(re v,i=fi[u];i;i=e[i].nx)
        if((v=e[i].to)!=fa[u]){
            fa[v]=u;dfs1(v);sz[u]+=sz[v];
            if(sz[v]>sz[son[u]])son[u]=v;
        }
}
void dfs2(re u){
    if(son[fa[u]]==u)top[u]=top[fa[u]];
    else top[u]=u;
    if(son[u])dfs2(son[u]);
    for(re v,i=fi[u];i;i=e[i].nx)
        if((v=e[i].to)!=fa[u]&&v!=son[u])
            dfs2(v);    
}
inline int lca(re u,re v){//用树剖找lca
    for(;top[u]!=top[v];dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]]);
    return dep[u]<dep[v]?u:v;
}
bool solve(re u,re v,re lc){//处理那两种情况
    if(dep[u]+dep[v]-2*dep[lc]>47)return 1;tp=0;
    while(u!=lc)stk[++tp]=w[u],u=fa[u];
    while(v!=lc)stk[++tp]=w[v],v=fa[v];
    stk[++tp]=w[lc];sort(stk+1,stk+tp+1);
    fp(i,2,tp-1)if(stk[i-1]>stk[i+1]-stk[i])return 1;
    return 0;
}
int main(){
    file("tri");
    sdf(n);sdf(m);dep[1]=1;fp(i,1,n)sdf(w[i]);
    fp(i,1,n-1){re u,v;sdf(u),sdf(v);add(u,v),add(v,u);}
    //加边
    dfs1(1);dfs2(1);
    fp(i,1,m){
        re op,u,v;sdf(op);sdf(u);sdf(v);
        if(op)w[u]=v;
        else puts(solve(u,v,lca(u,v))?"Y":"N");
    }
return 0;
}
/*
最坏的情况1 1 2 3...最多47层就爆int了
所以只要dis(u,v)>47就return 1
剩下的就暴力算吧o(5*47q)还是可以过的 
*/

小结:我猜着了开头,但猜不着结尾。考试时想到了要去用树剖做,但是是纯暴力,没加一点优化。然后加一点数学分析,就过了。这说明什么,数学很重要,也许今年NOIP数学会考得很活(火)!得多训练训练数学思维!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值