zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    最长公共前缀
    无重复字符的最长子串
    文章采集代码
    网络验证常见的攻击方式与防御手段
    初创公司如何避免服务器被攻击
    拒绝ssh远程暴力破解
    我公司开了7年,靠的就是这套顶级销售打法撑下来!
    顶级销售的十个习惯,轻松签下百万千万合同!(值得背下来)
    顶级销售高手总结的 9 个方面
    一位顶级销售高手总结的“销售心得”!
  • 原文地址:https://www.cnblogs.com/zquzjx/p/10006881.html
Copyright © 2011-2022 走看看