[LNOI 2014] LCA

题目传送-Luogu4211

题目传送-BZOJ3626

题意:

给你一棵\(n\)个节点的树,定义一个点的深度为它到1号节点的距离+1
\(q\)次询问,每次给出\((l,r,p)\),求\(\sum_{i=l}^rdep(LCA(i,p))\)

题解:

考虑计算LCA(x,y),我们把x->1经过的节点权值+1,那么答案就是y->1经过的权值和,这东西可以前缀一下,然后答案就是\(sum_r-sum_{l-1}\)
这个可以用离线+树剖搞出来

过程:

树剖写错了O(1)次
还忘了取模。。

代码:

const int N=50010;
const int P=201314;
int n,m;
namespace SHU {
    int head[N],nxt[N<<1],to[N<<1],lst=0;
    inline void adde(int x,int y) {
        nxt[++lst]=head[x]; to[lst]=y; head[x]=lst;
    }
    int fa[N],dep[N],sz[N],son[N],top[N],s_t[N],t_s[N],ind=0;
    void dfs1(int u) {
        sz[u]=1;
        int pos=-1;
        for(int i=head[u];i;i=nxt[i]) {
            int v=to[i];
            dep[v]=dep[u]+1;
            dfs1(v);
            sz[u]+=sz[v];
            if(pos==-1 || sz[pos]<sz[v]) pos=v;
        }
        son[u]=pos;
    }
    void dfs2(int u) {
        // printf("u=%d\n",u);
        t_s[u]=++ind; s_t[ind]=u;
        if(son[u]!=-1) {
            top[son[u]]=top[u];
            dfs2(son[u]);
        }
        for(int i=head[u];i;i=nxt[i])
            if(to[i]!=son[u]) {
                int v=to[i];
                top[v]=v;
                dfs2(v);
            }
    }
    inline void Init() {
        fa[1]=0; dep[0]=0;
        dep[1]=1; top[1]=1;
        dfs1(1); dfs2(1);
    }
    #define lc (u<<1)
    #define rc (lc|1)
    #define left lc,l,mid
    #define right rc,mid+1,r
    #define mid ((l+r)>>1)
    #define root 1,1,n
    const int ALL=N<<2;
    int val[ALL],laz[ALL];
    inline void Update(int u,int l,int r,int v) {
        (laz[u]+=v)%=P; (val[u]+=v*(r-l+1)%P)%=P;
    }
    inline void Push_Dn(int u,int l,int r) {
        if(laz[u]) {
            Update(left,laz[u]);
            Update(right,laz[u]);
            laz[u]=0;
        }
    }
    void modify(int u,int l,int r,int L,int R,int v) {
    //  assert(L<=R);
    //  printf("%d %d %d %d %d %d\n",u,l,r,L,R,v);
        if(L<=l && r<=R) {
            Update(u,l,r,v); return;
        }
        Push_Dn(u,l,r);
        if(L<=mid) modify(left,L,R,v);
        if(R> mid) modify(right,L,R,v);
        val[u]=(val[lc]+val[rc])%P;
    }
    int query(int u,int l,int r,int L,int R) {
        if(L<=l && r<=R) return val[u];
        Push_Dn(u,l,r);
        int ret=0;
        if(L<=mid) ret+=query(left,L,R);
        if(R> mid) ret+=query(right,L,R);
        return ret%P;
    }
    inline void Modify(int u) {
        while(dep[top[u]]!=0) {
            // printf("Modify::%d %d\n",u,top[u]);
            modify(root,t_s[top[u]],t_s[u],1);
            u=fa[top[u]];
        }
        // printf("val=%d\n",val[1]);
    }
    inline int Query(int u) {
        int ret=0;
        while(dep[top[u]]!=0) {
            ret=(ret+query(root,t_s[top[u]],t_s[u]))%P;;
            // printf("%d %d %d\n",u,top[u],ret);
            u=fa[top[u]];
        }
        return ret;
    }
}
struct QUERY {
    int l,r,p,ans;
    inline void in() {
        read(l); read(r); read(p); ++l; ++r; ++p;
    }
}a[N];
struct STA {
    int p,u,id,type;
    bool operator < (const STA &a)const {
        return p<a.p || (p==a.p && u<a.u);
    }
}q[N<<1]; int ind=0;
signed main() {
    // freopen("3.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n); read(m);
    for(int i=2;i<=n;i++) {
        read(SHU::fa[i]); ++SHU::fa[i];
        SHU::adde(SHU::fa[i],i);
    }
    SHU::Init();
    for(int i=1;i<=m;i++) {
        a[i].in();
        q[++ind]=(STA) {a[i].l-1,a[i].p,i,-1};
        q[++ind]=(STA) {a[i].r  ,a[i].p,i, 1};
    }
    sort(q+1,q+ind+1);
    int cur=1;
    while(q[cur].p==0 && cur<=ind) ++cur;
    for(int i=1;i<=n;i++) {
        // printf("i=%d\n",i);
        SHU::Modify(i);
        while(q[cur].p==i && cur<=ind) {
            (a[q[cur].id].ans+=q[cur].type*SHU::Query(q[cur].u))%=P;
            // printf("%d\n",a[q[cur].id].ans);
            ++cur;
        }
    }
    for(int i=1;i<=m;i++) {
        printf("%d\n",(a[i].ans+P)%P);
    }
    return 0;
}

用时:1h

转载于:https://www.cnblogs.com/functionendless/p/9546803.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值