P3398 仓鼠找sugar (LCA+树上路径交)

题目链接
题目大意:给你一棵树,然后还有q个询问,每次询问给出四个点,前两个点组成一条路径,后两个点一条,问这两条路径是否相交。
第一反应是树链剖分暴力修改和查询,事实证明这种做法也可以过,就是时间消耗大点,1s时限最后最大的测试点用了500多ms,数据大点估计就挂了。
但是我点开这个题标签一看,居然没有树剖,仔细一想,确实是,如果两条路径有交的话,那么其中两点的lca必然在另一条路径上,那么问题就变成了判断点是否在路径上。
那么假设要判断x是否在路径ab上,很简单,如果满足这个式子才有可能在路径ab上:dis(a,b)==dis(x,a)+dis(x,b)
那么问题就结束了,下面是代码。因为之前刚打了个树剖,所以lca就用树剖来求了,可以改成倍增。

#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
#include<cmath>
#include<string>
#include<fstream>
#include<map>
#include<algorithm>
#include<iostream>
#define lk (k<<1)
#define rk (k<<1|1)
using namespace std;
typedef long long ll;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const int N=1e5+10;
struct edge
{
    int v,next;
}e[N<<1];
int head[N],cnt;
void add(int u,int v)
{
    e[++cnt].v=v;
    e[cnt].next=head[u];
    head[u]=cnt;
}
int n,m;
int dep[N],fa[N],son[N];
int size[N],top[N];
void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n-1;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
}
void dfs1(int u,int f)
{
    dep[u]=dep[f]+1;
    size[u]=1;
    fa[u]=f;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==f) continue;
        dfs1(v,u);
        size[u]+=size[v];
        if(size[v]>size[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int f)
{
    if(son[u]){
        top[son[u]]=top[u];
        dfs2(son[u],u);
    }
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==f) continue;
        if(!top[v]){
            top[v]=v;
            dfs2(v,u);
        }
    }
}
int lca(int u,int v)
{
    int fu=top[u],fv=top[v];
    while(fu!=fv){
        if(dep[fu]<dep[fv]) swap(fu,fv),swap(u,v);
        u=fa[fu];
        fu=top[u];
    }
    if(dep[u]>dep[v]) swap(u,v);
    return u;
}
int dis(int x,int y)
{
    return dep[x]+dep[y]-2*dep[lca(x,y)];
}
void solve()
{
    while(m--){
        int a,b,c,d;
        scanf("%d%d%d%d",&a,&b,&c,&d);
        int x=lca(a,b);
        int y=lca(c,d);
        if(dis(a,b)==dis(y,a)+dis(y,b)){
            puts("Y");
        }else if(dis(c,d)==dis(x,c)+dis(x,d)){
            puts("Y");
        }else puts("N");
    }
}
int main()
{
    init();
    dfs1(1,0);
    fa[1]=1;
    top[1]=1;
    dfs2(1,0);

    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值