【BZOJ-3626】LCA 树链剖分

3626: [LNOI2014]LCA

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1428  Solved: 526
[Submit][Status][Discuss]

Description

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)

Input

第一行2个整数n q。
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。

Output

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

Sample Input

5 2
0
0
1
1
1 4 3
1 4 2

Sample Output

8
5

HINT

共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。

Source

数据已加强 by saffah

Solution

先是两篇高端的题解:HZW学长  TimeMachine学长

什么LCA,都是骗人的

直接暴力去做,很显然不可以,那么这必然会有一些性质或者转化使之简便

那么考虑一种其他的做法

离线处理,把每个询问拆成两个部分,分别是【1~l-1】和【1~r】那么前缀和?

那么维护一下信息,即z点到根的路径

然后加每个点的时候是把从这个点到根的路径的点权全部+1,然后查询就是查询某个点到根的路径的点权和

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define mod 201314
#define maxn 100100
#define maxq 100100
struct Edgenode{int to,next;}edge[maxn];
int head[maxn],cnt;int n,q,zz;
void add(int u,int v)
{cnt++;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt;}
//
int size[maxn],pre[maxn],deep[maxn],son[maxn],fa[maxn],pl[maxn],sz,pr[maxn],top[maxn];
void dfs_1(int x)
{
    size[x]=1;
    for(int i=head[x];i;i=edge[i].next)
        if (edge[i].to!=fa[x])
            {
                deep[edge[i].to]=deep[x]+1;
                fa[edge[i].to]=x;
                dfs_1(edge[i].to);
                size[x]+=size[edge[i].to];
            }
}
void dfs_2(int x,int chain)
{
    top[x]=chain; pl[x]=++sz;
    int k=n;
    for(int i=head[x];i;i=edge[i].next)
        if(edge[i].to!=fa[x]&&size[edge[i].to]>size[k])
            k=edge[i].to;
    if(k!=n) dfs_2(k,chain);
    for(int i=head[x];i;i=edge[i].next)
        if(edge[i].to!=fa[x]&&edge[i].to!=k)
            dfs_2(edge[i].to,edge[i].to);
}
//
struct Treenode{int l,r,tag,da;}tree[maxn<<2];
inline void update(int now)
{tree[now].da=tree[now<<1].da+tree[now<<1|1].da;}
inline void pushdown(int now)
{
    int l=tree[now].l,r=tree[now].r,tag=tree[now].tag;    
    int mid=(l+r)>>1,ln=mid-l+1,rn=r-mid;
    if (tag)
        {
            tree[now].tag=0;
            tree[now<<1].tag+=tag; tree[now<<1|1].tag+=tag;
            tree[now<<1].da+=ln*tag; tree[now<<1|1].da+=rn*tag;
        }
}
void build(int now,int l,int r)
{
    tree[now].tag=tree[now].da=0;
    tree[now].l=l;tree[now].r=r;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(now<<1,l,mid); build(now<<1|1,mid+1,r);
}
void segment_change(int now,int L,int R)
{
    pushdown(now);
    if (L<=tree[now].l && R>=tree[now].r) 
        {tree[now].da+=(tree[now].r-tree[now].l+1);tree[now].tag++;return;}
    int mid=(tree[now].l+tree[now].r)>>1;
    if (L<=mid) segment_change(now<<1,L,R);
    if (R>mid) segment_change(now<<1|1,L,R);
    update(now);
}
int segment_ask(int now,int L,int R)
{
    pushdown(now);
    if (L<=tree[now].l && R>=tree[now].r) return tree[now].da;
    int mid=(tree[now].l+tree[now].r)>>1; int ans=0;
    if (L<=mid) ans+=segment_ask(now<<1,L,R);
    if (R>mid) ans+=segment_ask(now<<1|1,L,R);
    return ans;
}

//
void change(int x,int y)
{
    while (top[x]!=top[y])
        {
            segment_change(1,pl[top[x]],pl[x]);
            x=fa[top[x]];
        }
    segment_change(1,pl[y],pl[x]);
}
int query(int x,int y)
{
    int ans=0;
    while (top[x]!=top[y])
        {
            ans=(ans+segment_ask(1,pl[top[x]],pl[x]))%mod;
            x=fa[top[x]];            
        }
    ans=(ans+segment_ask(1,pl[y],pl[x]))%mod;
    return ans;
}
//
struct Asknode{int z,ans1,ans2;}ask[maxq];
struct Reqnode
{
    int p,id;bool f;
    bool operator < (const Reqnode & A) const
        {return p<A.p;}
}req[maxq<<1];
int main()
{
//    freopen("3626.in","r",stdin);
//    freopen("3626.out","w",stdout);
    n=read(),q=read();
    for (int x,i=1; i<=n-1; i++)
        x=read(),add(x,i);
    for (int a,b,c,i=1; i<=q; i++)
        {
            a=read(),b=read(),c=read();
            ask[i].z=c;
            zz++;req[zz].p=a-1;req[zz].id=i;req[zz].f=0;
            zz++;req[zz].p=b;req[zz].id=i;req[zz].f=1;
        }
    sort(req+1,req+zz+1);
//    for (int i=1; i<=zz; i++)
//        printf("%d %d %d
",req[i].p,req[i].id,req[i].f);
    build(1,1,n); dfs_1(0); dfs_2(0,0);
//    for (int i=0; i<=n; i++)
//        printf("%d %d %d %d %d %d
",i,fa[i],deep[i],pl[i],size[i],top[i]);
    int now=-1;
    for (int i=1; i<=zz; i++)
        {
            while (now<req[i].p)
                now++,change(now,0);
//            printf("%d
",query(ask[req[i].id].z,0));
            if (!req[i].f) ask[req[i].id].ans1=query(ask[req[i].id].z,0);
            else ask[req[i].id].ans2=query(ask[req[i].id].z,0);
        }
    for (int i=1; i<=q; i++)
        printf("%d
",(ask[i].ans2-ask[i].ans1+mod)%mod);
    return 0;
}

sb错误毁一生!!!!

数据生成器:

#include<cstdio>
#include<ctime>
#include<cstdlib>
using namespace std;
int main()
{
    freopen("3626.in","w",stdout);
    int n;
    srand(time(0));
//    scanf("%d",&n);
    n=40000;
    printf("%d %d
",n,n);
    for (int i=0;i<=n-2;i++)
        printf("%d
",rand()%(i+1));
    for (int i=1;i<=n;i++)
        printf("%d %d %d
",rand()%n,rand()%n,rand()%n);
    return 0;
}
原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5356773.html