POJ 4718 /// 树链剖分+线段树区间合并 求树上两点间的LCIS长度

Posted zquzjx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ 4718 /// 树链剖分+线段树区间合并 求树上两点间的LCIS长度相关的知识,希望对你有一定的参考价值。

题目大意:

给定n个点 每个点都有权值

接下来给定树的n条边 第 i 个数 a[i] 表示 i+1到a[i]之间 有一条边

给定q q个询问 每次询问给出 x y 求x到y的最长上升子序列的长度

 

题解 https://blog.csdn.net/forever_wjs/article/details/52088861 

明确几个变量的定义之后 更新部分一看就懂 就不注释了

需要特别提到的是

我们每次合并区间是合并 新的要合并的区间(左子区间) 和 底部已合并好的区间(右子区间)

而我们在查询过程中两个点由底部不断向LCA逼近

这样当x和y逼近到LCA时 x和y对应区间的方向是 由LCA到x 由LCA到y 这样的两个区间

所以这两个区间不能进行合并 因为起点相同

所以 就应该利用

x对应区间的 由右端点始下降的LCIS 和 右端点的值

y对应区间的 有左端点始上升的LCIS 和 左端点的值

再更新一次答案

技术分享图片
#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f
#define mem(i,j) memset(i,j,sizeof(i))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define root 1,n,1

const int maxn=1e5+5;
int n, q, w[maxn];

struct IntervalTree {
    struct EDGE { int to,ne; }e[maxn<<1];
    int head[maxn], tot;
    void addE(int u,int v) {
        e[tot].to=v;
        e[tot].ne=head[u];
        head[u]=tot++;
    }

    int fa[maxn], son[maxn], dep[maxn], num[maxn];
    int top[maxn], p[maxn], fp[maxn], pos;

    void init() {
        tot=1; mem(head,0);
        pos=0; mem(son,0);
    }

    struct TREE {
        int l,r; // 区间左右位置
        int Lw,Rw; // 左端点的值 右端点的值
        int LL,LR;
        // 以左端点始的下降LCIS长度 以左端点始的上升LCIS长度
        int RL,RR;
        // 以右端点始的下降LCIS长度 以右端点始的上升LCIS长度
        int Len,Ren;
        // 该区间的 从左上升LCIS长度 从右上升LCIS长度
        TREE(){ l=r=Lw=Rw=LL=LR=RL=RR=Len=Ren=0; }
    }tree[maxn<<2];

// --------------------以下是线段树-------------------------

    TREE Merge(TREE L,TREE R) {
        if(R.Len==0) return L;
        TREE ans=L;
        ans.r=R.r, ans.Rw=R.Rw;
        ans.Len=max(L.Len,R.Len);
        ans.Ren=max(L.Ren,R.Ren);
        if(L.LL==L.r-L.l+1 && L.Rw>R.Lw) ans.LL=L.LL+R.LL;
        else ans.LL=L.LL;
        if(L.LR==L.r-L.l+1 && L.Rw<R.Lw) ans.LR=L.LR+R.LR;
        else ans.LR=L.LR;
        if(R.RL==R.r-R.l+1 && L.Rw<R.Lw) ans.RL=R.RL+L.RL;
        else ans.RL=R.RL;
        if(R.RR==R.r-R.l+1 && L.Rw>R.Lw) ans.RR=R.RR+L.RR;
        else ans.RR=R.RR;
        ans.Len=max(ans.Len,ans.LR);
        ans.Len=max(ans.Len,ans.RL);
        ans.Ren=max(ans.Ren,ans.LL);
        ans.Ren=max(ans.Ren,ans.RR);
        if(L.Rw<R.Lw) ans.Len=max(ans.Len,L.RL+R.LR);
        if(L.Rw>R.Lw) ans.Ren=max(ans.Ren,L.RR+R.LL);
        return ans;
    }
    void build(int l,int r,int rt) {
        tree[rt].l=l, tree[rt].r=r;
        if(l==r) {
            tree[rt].LL=tree[rt].LR=1,
            tree[rt].RL=tree[rt].RR=1,
            tree[rt].Len=tree[rt].Ren=1;
            tree[rt].Lw=tree[rt].Rw=fp[l];
            return;
        }
        int m=(l+r)>>1;
        build(lson), build(rson);
        tree[rt]=Merge(tree[rt<<1],tree[rt<<1|1]);
    }
    TREE query(int L,int R,int l,int r,int rt) {
        if(L==l && r==R) return tree[rt];
        int m=(l+r)>>1;
        if(R<=m) return query(L,R,lson);
        else if(L>m) return query(L,R,rson);
        else return Merge(query(L,m,lson),query(m+1,R,rson));
    }

// --------------------以上是线段树-------------------------

// --------------------以下是树链剖分-------------------------

    void dfs1(int u,int pre,int d) {
        dep[u]=d; fa[u]=pre; num[u]=1;
        for(int i=head[u];i;i=e[i].ne) {
            int v=e[i].to;
            if(v!=fa[u]) {
                dfs1(v,u,d+1);
                num[u]+=num[v];
                if(!son[u] || num[v]>num[son[u]])
                    son[u]=v;
            }
        }
    }
    void dfs2(int u,int sp) {
        top[u]=sp; p[u]=++pos; fp[p[u]]=w[u];
        if(!son[u]) return;
        dfs2(son[u],sp);
        for(int i=head[u];i;i=e[i].ne) {
            int v=e[i].to;
            if(v!=son[u] && v!=fa[u])
                dfs2(v,v);
        }
    }
    int solve(int x,int y) {
        int fx=top[x], fy=top[y];
        TREE ans1, ans2;
        bool flag=0;
        while(fx!=fy) {
            if(dep[fx]>dep[fy]) {
                ans1=Merge(query(p[fx],p[x],root),ans1);
                x=fa[fx];
            } else {
                ans2=Merge(query(p[fy],p[y],root),ans2);
                y=fa[fy];
            }
            fx=top[x], fy=top[y];
        }
        if(p[x]>p[y]) ans1=Merge(query(p[y],p[x],root),ans1);
        else ans2=Merge(query(p[x],p[y],root),ans2);
        int ans=max(ans1.Ren,ans2.Len);
        if(ans1.Lw<ans2.Lw) ans=max(ans,ans1.LL+ans2.LR);
        return ans;
    }

// --------------------以上是树链剖分-------------------------

    void initQTree() {
        dfs1(1,0,0), dfs2(1,1);
        build(root);
    }
}T; 

int main()
{
    int t, tcase=1;
    scanf("%d",&t);
    bool st=0;
    while(t--) {
        if(st) puts("");
        scanf("%d",&n);
        T.init();
        for(int i=1;i<=n;i++) scanf("%d",&w[i]);
        for(int i=2;i<=n;i++) {
            int v; scanf("%d",&v);
            T.addE(i,v); T.addE(v,i);
        }
        T.initQTree();
        scanf("%d",&q);
        printf("Case #%d:
",tcase++);
        while(q--) {
            int u,v; scanf("%d%d",&u,&v);
            printf("%d
",T.solve(u,v));
        } st=1;
    }

    return 0;
}
View Code

 

以上是关于POJ 4718 /// 树链剖分+线段树区间合并 求树上两点间的LCIS长度的主要内容,如果未能解决你的问题,请参考以下文章

(树链剖分+线段树)POJ - 3237 Tree

BZOJ 2243: [SDOI2011]染色 树链剖分+线段树区间合并

POJ 3237 /// 树链剖分 线段树区间修改(*-1)

POJ3237-Tree (树链剖分,线段树区间更新+点更新+区间查询)

bzoj 2243: [SDOI2011]染色 线段树区间合并+树链剖分

bzoj 2243: [SDOI2011]染色 (树链剖分+线段树 区间合并)