洛谷 P4211 [LNOI2014]LCA

洛谷 P4211 [LNOI2014]LCA

洛谷传送门

题目描述

给出一个 nn 个节点的有根树(编号为 00 到 n-1n−1,根节点为 00)。

一个点的深度定义为这个节点到根的距离 +1+1。

设 dep[i]dep[i] 表示点i的深度,LCA(i,j)LCA(i,j) 表示 ii 与 jj 的最近公共祖先。

有 qq 次询问,每次询问给出 l\ r\ zl r z,求 \sum_{i=l}^r dep[LCA(i,z)]∑i=lrdep[LCA(i,z)] 。

输入格式

第一行 22 个整数,n, qn,q

接下来 n-1n−1 行,分别表示点 11 到点 n-1n−1 的父节点编号。

接下来 qq 行,每行 33 个整数,l, r, zl,r,z

输出格式

输出 qq 行,每行表示一个询问的答案。每个答案对 201314201314 取模输出。


题解:

题目很简洁。一看就是好题。

首先,我们可以得到,任意节点和已知节点的LCA一定在已知节点到根节点的这条路径上。

我们可以深入思考:

深度是什么?深度就是当前点上面(包括自己)还有几个节点。那么我们完全可以把深度统计变成路径统计。既然deep[LCA]是LCA到根节点的距离,那么,求和的时候,我们完全可以对于\([l,r]\)区间,都把根节点到当前节点的路径的节点们权值全部加一。那么处理完毕之后,只需要跑目标节点\(z\)到树根的路径和就行。

芜湖,链上修改+查询。树链剖分架线段树。

分析复杂度:这样的复杂度,一次是\(O(n\log^2)\)的,\(n\)次就是\(O(n^2\log^2)\)

这个数据范围是\(O(n\log^2)\)的数据范围,那么我们想着如何优化掉一个n。

我们发现,\(n^2\log^2\)算法多出来的n消耗在重构线段树上,那么,如果我们连续处理每个询问呢?想到离线区间排序之后连续处理。但是这样的处理无法同时维护两个端点的变化。于是我们尝试着把一个区间端点固定为1.因为以上的性质满足区间减法,也就是\([1,r]-[1,l)\),所以可以通过这样的优化,只跑一遍修改即可。优化了一个n。

具体实现详见代码:

#include<cstdio>
#include<algorithm>
#define int long long
#define lson pos<<1
#define rson pos<<1|1
using namespace std;
const int maxn=5*1e5+10;
const int mod=201314;
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read()
{
    int x=0,f=1;
    char ch=nc();
    while(ch<48||ch>57)
        f=-1,ch=nc();
    while(ch>=48&&ch<=57)
        x=x*10+ch-48,ch=nc();
    return x*f;
}
int n,m;
int fa[maxn],ans[maxn];
int tot,to[maxn<<1],nxt[maxn<<1],head[maxn];
int cnt;
int deep[maxn],size[maxn],son[maxn],top[maxn],id[maxn],num;
int tree[maxn<<2],lazy[maxn<<2];
struct node
{
    bool flag;
    int id,r,z;
}q[maxn<<1];
bool cmp(node a,node b)
{
    return a.r<b.r;
}
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void dfs1(int x,int f)
{
    deep[x]=deep[f]+1;
    size[x]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==f)
            continue;
        dfs1(y,x);
        size[x]+=size[y];
        if(!son[x]||size[y]>size[son[x]])
            son[x]=y;
    }
}
void dfs2(int x,int t)
{
    top[x]=t;
    id[x]=++num;
    if(!son[x])
        return;
    dfs2(son[x],t);
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==fa[x]||y==son[x])
            continue;
        dfs2(y,y);
    }
}
void mark(int pos,int l,int r,int k)
{
    tree[pos]=(tree[pos]+(r-l+1)*k)%mod;
    lazy[pos]+=k;
}
void pushdown(int pos,int l,int r)
{
    int mid=(l+r)>>1;
    mark(lson,l,mid,lazy[pos]);
    mark(rson,mid+1,r,lazy[pos]);
    lazy[pos]=0;
}
void update(int pos,int l,int r,int x,int y)
{
    int mid=(l+r)>>1;
    if(x<=l && r<=y)
    {
        mark(pos,l,r,1);
        return;
    }
    if(lazy[pos])
        pushdown(pos,l,r);
    if(x<=mid)
        update(lson,l,mid,x,y);
    if(y>mid)
        update(rson,mid+1,r,x,y);
    tree[pos]=(tree[lson]+tree[rson])%mod;
}
void upd_chain(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])
            swap(x,y);
        update(1,1,n,id[top[x]],id[x]);
        x=fa[top[x]];
    }
    if(deep[x]<deep[y])
        swap(x,y);
    update(1,1,n,id[y],id[x]);
}
int query(int pos,int l,int r,int x,int y)
{
    int ret=0;
    int mid=(l+r)>>1;
    if(x<=l && r<=y)
        return tree[pos]%mod;
    if(lazy[pos])
        pushdown(pos,l,r);
    if(x<=mid)
        ret=(ret+query(lson,l,mid,x,y))%mod;
    if(y>mid)
        ret=(ret+query(rson,mid+1,r,x,y))%mod;
    return ret%mod;
}
int q_chain(int x,int y)
{
    int ret=0;
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])
            swap(x,y);
        ret=(ret+query(1,1,n,id[top[x]],id[x]))%mod;
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])
        swap(x,y);
    ret=(ret+query(1,1,n,id[x],id[y]))%mod;
    return ret%mod;
}
signed main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=2;i<=n;i++)
    {
        scanf("%lld",&fa[i]);
        fa[i]++;
        add(fa[i],i);
        add(i,fa[i]);
    }
    dfs1(1,0);
    dfs2(1,1);
    for(int i=1;i<=m;i++)
    {
        int l,r,z;
        scanf("%lld%lld%lld",&l,&r,&z);
        r++,z++;
        q[++cnt].flag=0,q[cnt].id=i,q[cnt].r=l,q[cnt].z=z;
        q[++cnt].flag=1,q[cnt].id=i,q[cnt].r=r,q[cnt].z=z;
    }
    sort(q+1,q+cnt+1,cmp);
    q[0].r=0;
    for(int i=1;i<=cnt;i++)
    {
        for(int j=q[i-1].r+1;j<=q[i].r;j++)
            upd_chain(1,j);
        if(!q[i].flag)
            ans[q[i].id]=(ans[q[i].id]-q_chain(1,q[i].z)+mod)%mod;
        else
            ans[q[i].id]=(ans[q[i].id]+q_chain(1,q[i].z))%mod;
    }
    for(int i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
    return 0;
}
posted @ 2020-10-10 11:15  Seaway-Fu  阅读(168)  评论(0编辑  收藏  举报