[LNOI2014] LCA

59 篇文章 0 订阅
22 篇文章 0 订阅

题目描述:

给出一颗树
根节点为 1
给出 M 个 询问
l r z
ri=ldep[lca[i,z]] ∑ i = l r d e p [ l c a [ i , z ] ]
dep为距离根节点的距离+1

题目分析:

暴力分一点不给差评!
思路来自黄学长
首先 考虑一种暴力
我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。
观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] − [1, l − 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n − 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。
资瓷这样的显然是树剖或者LCT
树剖O((n+q)*logn*logn)

题目链接:

Luogu 4211
BZOJ 3626

Ac 代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#define ls (o<<1)
#define rs (o<<1)|1
const int mod=201314;
const int maxm=200005;
struct node1{
    int p,flag,id;
}ask[maxm*2];
struct node2{
    int z,ans[2];
}q[maxm];
struct node3{
    int add,sum,sizx;
}st[maxm<<2];
int deep[maxm],son[maxm],top[maxm],id[maxm],fa[maxm],siz[maxm],tot,num;
int head[maxm],to[maxm],net[maxm],cnt;
int n,m;
void addedge(int u,int v)
{
    cnt++;
    to[cnt]=v,net[cnt]=head[u],head[u]=cnt;
}
void dfs1(int now,int fax,int dep)
{
    deep[now]=dep,fa[now]=fax,siz[now]=1;
    for(int i=head[now];i;i=net[i])
    if(to[i]!=fax)
    {
        dfs1(to[i],now,dep+1);
        siz[now]+=siz[to[i]];
        if(siz[to[i]]>siz[son[now]]) son[now]=to[i]; 
    }
}
void dfs2(int now,int topx)
{
    top[now]=topx;
    id[now]=++tot;
    if(son[now]) dfs2(son[now],topx);
    for(int i=head[now];i;i=net[i])
    if(!id[to[i]])
     dfs2(to[i],to[i]);
}
void updata(int o){st[o].sum=(st[ls].sum+st[rs].sum)%mod;}
void col(int o,int num)
{
    int sizx=st[o].sizx;
    st[o].add=(st[o].add+num)%mod;
    st[o].sum=(st[o].sum+1ll*sizx*num%mod)%mod;
}
void pushdown(int o)
{
    if(st[o].add)
    {
        col(ls,st[o].add);
        col(rs,st[o].add);
        st[o].add=0;
    }
}
void build(int o,int l,int r)
{
    st[o].sizx=(r-l+1);
    if(l>=r) return;
    int mid=(l+r)>>1;
    build(ls,l,mid),build(rs,mid+1,r);
}
void change(int o,int l,int r,int ql,int qr,int num)
{
    if(ql<=l&&r<=qr)
    {
        col(o,num);
        return;
    }
    pushdown(o);
    int mid=(l+r)>>1;
    if(ql<=mid) change(ls,l,mid,ql,qr,num);
    if(qr>mid) change(rs,mid+1,r,ql,qr,num);
    updata(o);
}
int asksum(int o,int l,int r,int ql,int qr)
{
    if(ql<=l&&r<=qr) return st[o].sum;
    pushdown(o);
    int mid=(l+r)>>1;
    int ans1=0,ans2=0;
    if(ql<=mid) ans1=asksum(ls,l,mid,ql,qr);
    if(qr>mid) ans2=asksum(rs,mid+1,r,ql,qr);
    return (ans1+ans2)%mod;
} 
inline void addtree(int u,int v)
{
    while(top[u]!=top[v])
    {
        if(deep[top[u]]<deep[top[v]]) std::swap(u,v);
        change(1,1,n,id[top[u]],id[u],1);
        u=fa[top[u]];
    }
    if(deep[u]<deep[v]) std::swap(u,v);
    change(1,1,n,id[v],id[u],1);
}
inline int asktree(int u,int v)
{
    int ans=0;
    while(top[u]!=top[v])
    {
        if(deep[top[u]]<deep[top[v]]) std::swap(u,v);
        ans=(ans+asksum(1,1,n,id[top[u]],id[u])%mod)%mod;
        u=fa[top[u]];
    }
    if(deep[u]<deep[v]) std::swap(u,v);
    ans=(ans+asksum(1,1,n,id[v],id[u])%mod)%mod;
    return ans%mod;
}
bool comp(node1 x,node1 y)
{
    return x.p<y.p;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=2;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        x++;
        addedge(x,i);
    }
    for(int i=1;i<=m;i++)
    {
        int l,r,z;
        scanf("%d%d%d",&l,&r,&z);
        l++,r++,z++;
        q[i].z=z;
        ask[++num].p=l-1,ask[num].flag=0,ask[num].id=i;
        ask[++num].p=r,ask[num].flag=1,ask[num].id=i;
    }
    build(1,1,n);
    std::sort(ask+1,ask+num+1,comp);
    dfs1(1,0,0),dfs2(1,1);
    int now=0;
    for(int i=1;i<=num;i++)
    {
        while(now<ask[i].p) addtree(++now,1);
        int x=ask[i].id;
        int d;
        q[x].ans[ask[i].flag]=d=asktree(q[x].z,1);
        //printf("%d\n",d);
    }
    for(int i=1;i<=m;i++) printf("%d\n",(q[i].ans[1]-q[i].ans[0]+mod)%mod);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值