• BZOJ3626: [LNOI2014]LCA


    BZOJ3626: [LNOI2014]LCA

    Description

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

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

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

    有q次询问,每次询问给出l r 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。

    题解Here!

    又是树上的询问,首选 树链剖分/LCT。

    这题就是树链剖分+线段树+差分。

    deep[i]是什么?——就是从 i 点到根有多少个点(包括 i )。

    我们从整体上考虑,发现对于一个询问:l , r , z 来说,所有的 lca 都在 z 到根的路径上。

    从而有一些点,它们对很多的 lca 的深度都有贡献,而这个贡献等于在这个点下面的 lca 的个数,所以我们可以把每个 lca 到根的路径上的每个点的权值都加一。

    然后从 z 向上走到根,沿路统计的权值就是答案了。

    就是:对于一个询问: l , r , z ,我们把每个点 i ( l <= i <= r ) 到根的路径上的每一个点的权值都加一,因为:

    所有的 lca 都在 z 到根的路径上,所以我们可以从 z 点向上爬到根,沿途统计的点权值之和,就是答案了。

    我们每次的操作都是从某个点到根的,所以树链剖分+线段树就好了。

    但是我们每次清空线段树,然后从 l ~ r 再添加一遍,树剖+线段树的复杂度就是 n*(logn)^2 的,还要做 q 次,复杂度依然不理想。

    于是想到:差分可以将询问拆开,而且每个拆开的区间之间是有重叠的,是可以转移的,而不用每次都清空。

    而且差分后的数组只与右端点有关!

    所以我们可以将差分后的区间按照右端点从小到大排序(左端点都是根),然后按从小到大的顺序添加点,每遇到一个询问就查询一次。

    于是一波sao操作就A了。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define LSON rt<<1
    #define RSON rt<<1|1
    #define DATA(x) b[x].data
    #define SIGN(x) b[x].c
    #define LSIDE(x) b[x].l
    #define RSIDE(x) b[x].r
    #define WIDTH(x) (RSIDE(x)-LSIDE(x)+1)
    #define MAXN 50010
    #define MOD 201314 
    using namespace std;
    int n,m,c=1,d=1,e=1;
    int head[MAXN],deep[MAXN],fa[MAXN],son[MAXN],size[MAXN],id[MAXN],top[MAXN];
    struct node1{
    	int next,to;
    }a[MAXN];
    struct node2{
        int data,c,l,r;
    }b[MAXN<<2];
    struct node3{
        int x,u,id;
        bool flag;
    }que[MAXN<<1];
    struct node4{
        int l,r;
    }ans[MAXN];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    bool cmp(const node3 &x,const node3 &y){
        if(x.x==y.x)return x.id<y.id;
        return x.x<y.x;
    }
    inline void add(int x,int y){
    	a[c].to=y;
    	a[c].next=head[x];
    	head[x]=c++;
    }
    inline void addque(int l,int r,int u,int i){
        que[e].x=l;que[e].u=u;que[e].id=i;
        que[e++].flag=false;
        que[e].x=r;que[e].u=u;que[e].id=i;
        que[e++].flag=true;
    }
    void dfs1(int rt){
    	son[rt]=0;size[rt]=1;
    	for(int i=head[rt];i;i=a[i].next){
    		int will=a[i].to;
    		if(!deep[will]){
    			deep[will]=deep[rt]+1;
    			fa[will]=rt;
    			dfs1(will);
    			size[rt]+=size[will];
    			if(size[son[rt]]<size[will])son[rt]=will;
    		}
    	}
    }
    void dfs2(int rt,int f){
    	id[rt]=d++;top[rt]=f;
    	if(son[rt])dfs2(son[rt],f);
    	for(int i=head[rt];i;i=a[i].next){
    		int will=a[i].to;
    		if(will!=fa[rt]&&will!=son[rt])
    		dfs2(will,will);
    	}
    }
    inline void pushup(int rt){
        DATA(rt)=(DATA(LSON)+DATA(RSON))%MOD;
    }
    inline void pushdown(int rt){
        if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return;
        SIGN(LSON)=(SIGN(LSON)+SIGN(rt))%MOD;
        DATA(LSON)=(DATA(LSON)+SIGN(rt)*WIDTH(LSON))%MOD;
        SIGN(RSON)=(SIGN(RSON)+SIGN(rt))%MOD;
        DATA(RSON)=(DATA(RSON)+SIGN(rt)*WIDTH(RSON))%MOD;
        SIGN(rt)=0;
    }
    void buildtree(int l,int r,int rt){
        int mid;
        LSIDE(rt)=l;
        RSIDE(rt)=r;
        if(l==r){
            DATA(rt)=0;
            return;
        }
        mid=l+r>>1;
        buildtree(l,mid,LSON);
        buildtree(mid+1,r,RSON);
        pushup(rt);
    }
    void update(int l,int r,int c,int rt){
        int mid;
        if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
            SIGN(rt)=(SIGN(rt)+c)%MOD;
            DATA(rt)=(DATA(rt)+c*WIDTH(rt))%MOD;
            return;
        }
        pushdown(rt);
        mid=LSIDE(rt)+RSIDE(rt)>>1;
        if(l<=mid)update(l,r,c,LSON);
        if(mid<r)update(l,r,c,RSON);
        pushup(rt);
    }
    int query(int l,int r,int rt){
        int mid,ans=0;
        if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
        pushdown(rt);
        mid=LSIDE(rt)+RSIDE(rt)>>1;
        if(l<=mid)ans=(ans+query(l,r,LSON)%MOD)%MOD;
        if(mid<r)ans=(ans+query(l,r,RSON)%MOD)%MOD;
        return ans%MOD;
    }
    void work_update(int x,int y,int k){
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]])swap(x,y);
            update(id[top[x]],id[x],1,1);
            x=fa[top[x]];
        }
        if(deep[x]>deep[y])swap(x,y);
        update(id[x],id[y],1,1);
        return;
    }
    int work_query(int x,int y){
        int s=0;
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]])swap(x,y);
            s=(s+query(id[top[x]],id[x],1)%MOD)%MOD;
            x=fa[top[x]];
        }
        if(deep[x]>deep[y])swap(x,y);
        s=(s+query(id[x],id[y],1)%MOD)%MOD;
        return s;
    }
    void work(){
        int now=1,id;
        for(int i=1;i<e;i++){
            while(now<=que[i].x){
                work_update(1,now,1);
                now++;
            }
            id=que[i].id;
            if(que[i].flag)ans[id].r=work_query(1,que[i].u);
            else ans[id].l=work_query(1,que[i].u);
        }
        for(int i=1;i<=m;i++)printf("%d
    ",(ans[i].r-ans[i].l+MOD)%MOD);
    }
    void init(){
    	int x,l,r,u;
    	n=read();m=read();
    	for(int i=2;i<=n;i++){
    		x=read()+1;
    		add(x,i);
    	}
    	deep[1]=1;
    	dfs1(1);
    	dfs2(1,1);
    	buildtree(1,n,1);
    	for(int i=1;i<=m;i++){
    		l=read();r=read()+1;u=read()+1;
    		addque(l,r,u,i);
    	}
    	sort(que+1,que+e,cmp);
    }
    int main(){
    	init();
    	work();
    	return 0;
    }
    
  • 相关阅读:
    添加定界符: AnsiQuotedStr
    c++ builder 中的 XMLDocument 类详解(13) 遍历XML文件
    c++ builder 中的 XMLDocument 类详解(11) 读取和设置版本号
    c++ builder 中的 XMLDocument 类详解(9) 关于 HasChildNodes 与 IsTextElement
    VBA 宏 与 Word 编程
    Iframe 高度自适应!
    第一次加载控件的问题.
    Server.Transfer 页面传值.
    Ajax 动态加载 用户控件脚本报 "缺少对象" 的错误!
    GridView的 RowCreated 里不能写有关控件的客户端事件属性!!
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9032258.html
Copyright © 2020-2023  润新知