HDU 4718 The LCIS on the Tree (树链剖分+线段树区间合并)

 

 

The LCIS on the Tree

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 1386    Accepted Submission(s): 404


 

Problem Description

 

For a sequence S1, S2, ... , SN, and a pair of integers (i, j), if 1 <= i <= j <= N and Si < Si+1 < Si+2 < ... < Sj-1 < Sj , then the sequence Si, Si+1, ... , Sj is a CIS (Continuous Increasing Subsequence). The longest CIS of a sequence is called the LCIS (Longest Continuous Increasing Subsequence).
Now we consider a tree rooted at node 1. Nodes have values. We have Q queries, each with two nodes u and v. You have to find the shortest path from u to v. And write down each nodes' value on the path, from u to v, inclusive. Then you will get a sequence, and please show us the length of its LCIS.

 

 

Input

 

The first line has a number T (T <= 10) , indicating the number of test cases.
For each test case, the first line is a number N (N <= 105), the number of nodes in the tree.
The second line comes with N numbers v1, v2, v3 ... , vN, describing the value of node 1 to node N. (1 <= vi <= 109)
The third line comes with N - 1 numbers p2, p3, p4 ... , pN, describing the father nodes of node 2 to node N. Node 1 is the root and will have no father.
Then comes a number Q, it is the number of queries. (Q <= 105)
For next Q lines, each with two numbers u and v. As described above.

 

 

Output

 

For test case X, output "Case #X:" at the first line.
Then output Q lines, each with an answer to the query.
There should be a blank line *BETWEEN* each test case.

 

 

Sample Input

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

 

 

Sample Output

Case #1:
3
2
3

 

 

Source

 

2013 ACM/ICPC Asia Regional Online —— Warmup2

 

 

 

 

 

 

题意:

给你一棵树和书上每一个点的权值

有q个询问,每次询问给你[a,b]问你a->b的路径中得到的序列的最大递增连续子序列是多少

 

 

解析:

树链剖分优化+线段树区间合并,就是在

因为a->b的路径的最大递增连续子序列是有顺序的,所以在用树链剖分的时候会打乱他的顺序,需要用一个flag来标记他的顺序

里面还有一些细节需要小心处理,譬如里面不仅要计算[l,r]递增的最长子序列,还要计算[l,r]递减的最长子序列,这里就是因为路径上的顺序和线段树上的顺序是不一致的

#include<cstdio>
#include<cstring>
#include<iostream>
#define INF 999999999
using namespace std;

const int MAXN = 2e5+10;
typedef struct TREE
{                //递增
    int rlen;   //从左边端点l开始满足条件的最大区间长度(包括r),在点表示的区间内
    int llen;    //从左边端点l开始满足条件的最大区间长度(包括l),在该点表示的区间内
    int all;    //rt对应区间即[l,r]内满足条件的区间的最大长度

    int irlen;   //递减
    int illen;   //
    int iall;
    int Lval,Rval;
    int l,r;

    TREE()
    {
        rlen=llen=all=irlen=illen=iall=Lval=Rval=l=r=0;
    }
}seg;
seg Btree[MAXN*4];
int stu[MAXN];

int MAX(int a,int b)
{
    return a<b?b:a;
}

int MIN(int a,int b)
{
    return a<b?a:b;
}


int val[MAXN],fa[MAXN],dep[MAXN],siz[MAXN],id[MAXN],top[MAXN],ran[MAXN],son[MAXN];

typedef struct ee
{
    int u,v;
    int next;
}ee;

ee edge[MAXN*2];
int head[MAXN],cnt;
int num,n;


void addedge(int u,int v)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

void dfs1(int x,int f,int d)
{
    dep[x]=d;
    siz[x]=1;
    son[x]=0;
    fa[x]=f;
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        int tmp=edge[i].v;
        if(tmp==f) continue;
        dfs1(tmp,x,d+1);
        siz[x]+=siz[tmp];
        if(siz[son[x]]<siz[tmp]||!son[x])
        {
            son[x]=tmp;
        }
    }
}

void dfs2(int x,int tp)
{
    top[x]=tp;
    id[x]=++num;
    ran[num]=x;
    if(son[x]) dfs2(son[x],tp);
    else return;
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        int tmp=edge[i].v;
        if(tmp==fa[x]||tmp==son[x]) continue;
        dfs2(tmp,tmp);
    }

}


void pushup(const seg A,const seg B,seg& ac/*,int flag*/)
{
    if(B.all==0)
    {
        ac=A;
        return;
    }

    ac.l=A.l;
    ac.r=B.r;
    ac.Lval=A.Lval;
    ac.Rval=B.Rval;
    //if(!flag)
    //{
        ac.llen=A.llen;  //从最左边向右算起
        ac.rlen=B.rlen;   //右孩子的右边向左算起
        ac.all=MAX(A.all,B.all);

        if(stu[ran[A.r]]<stu[ran[B.l]])  //合并
        {                                    //因为要求的区间是连续的所以必须等于区间长度
            if(ac.llen==A.r-A.l+1)   //lsum[rt<<1]也就是左子区间左边开始的最大长度,如果这个区间长度刚好是l到mid之间的长度
            {                                 //,(又因为可以合并,所以可以更新[root].llen)说明区间已经穿过中点了,应该在加上右子区间lsum[rt<<1|1]这部分
                ac.llen+=B.llen;
            }
            if(ac.rlen==B.r-B.l+1)
            {
                ac.rlen+=A.rlen;
            }
            ac.all=MAX(ac.all,B.llen+A.rlen);   //要中间连起来,则必须从左孩子的最右边向左数+右孩子的最左边向右数(连续)
        }
    //}
    //else
    //{
        ac.illen=A.illen;
        ac.irlen=B.irlen;
        ac.iall=MAX(A.iall,B.iall);

        if(stu[ran[A.r]]>stu[ran[B.l]])
        {
            if(ac.illen==A.r-A.l+1)   //lsum[rt<<1]也就是左子区间左边开始的最大长度,如果这个区间长度刚好是l到mid之间的长度
            {                                 //,(又因为可以合并,所以可以更新[root].illen)说明区间已经穿过中点了,应该在加上右子区间lsum[rt<<1|1]这部分
                ac.illen+=B.illen;
            }
            if(ac.irlen==B.r-B.l+1)
            {
                ac.irlen+=A.irlen;
            }
            ac.iall=MAX(ac.iall,B.illen+A.irlen);   //要中间连起来,则必须从左孩子的最右边向左数+右孩子的最左边向右数(连续)
        }
    //}
}

void build(int stu[],int l,int r,int root)  //l,r表示他们在stu中的下标,root表示他们在线段树中的坐标
{
    Btree[root<<1].l=l;
    Btree[root<<1|1].r=r;                                      //不是一颗完全二叉树
    if(l>r)return;
    if(l==r)
    {
        Btree[root].llen=Btree[root].rlen=Btree[root].all=1;
        Btree[root].illen=Btree[root].irlen=Btree[root].iall=1;

        Btree[root].l=Btree[root].r=l;
        Btree[root].Lval=Btree[root].Rval=stu[ran[l]];

        return;
    }

    int mid=(l+r)/2;

    build(stu,l,mid,root*2);
    build(stu,mid+1,r,root*2+1);

    pushup(Btree[root*2],Btree[root*2+1],Btree[root]);
}

seg query(int root,int s1,int e1,int l,int r)
{
    if(s1>=l&&e1<=r)
    {
        return Btree[root];
    }

    int mid=(s1+e1)/2;              //主要3.对区间的查找
    if(mid>=r)
    return query(root*2,s1,mid,l,r);
    else if(mid+1<=l)
    return query(root*2+1,mid+1,e1,l,r);
    else
    {
        seg ac;
        pushup(query(root*2,s1,mid,l,r),query(root*2+1,mid+1,e1,l,r),ac);
        return ac;

    }

}


void solvesum(int a,int b)
{
    int flag=1;
    seg ans1,ans2;
    if(dep[top[a]]<dep[top[b]])
    {
        swap(a,b);
        swap(ans1,ans2);
        flag^=1;
    }
    while(top[a]!=top[b])
    {
        //int tmp=top[a];
        pushup(query(1,1,n,id[top[a]],id[a]),ans1,ans1);

        a=fa[top[a]];
        if(dep[top[a]]<dep[top[b]])
        {
            swap(a,b);
            swap(ans1,ans2);
            flag^=1;
        }
    }
    if(id[a]>id[b]) swap(a,b),swap(ans1,ans2),flag^=1;
    pushup(query(1,1,n,id[a],id[b]),ans2,ans2);//b永远指向链上下面的那个点(即线段树上id大的),然后下面那个点要更新到a
    if(!flag) swap(ans1,ans2);//路径上的点,这一部分是属于下面那个点的部分
    int res=max(ans1.iall,ans2.all);
    if(ans1.Lval<ans2.Lval) res=max(ans1.illen+ans2.llen,res);

    printf("%d\n",res);
}

int main()
{
    int i,m,a,b,t,ans;
    scanf("%d",&t);
    int cas=0;
    while(t--)
    {
        if(cas)printf("\n");
        cas++;
        scanf("%d",&n);
        memset(Btree,0,sizeof(Btree));
        memset(head,-1,sizeof(head));
        //memset(son,0,sizeof(son));
        cnt=num=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&stu[i]);
        }
        for(int i=2;i<=n;i++)
        {
            int tmp;
            scanf("%d",&tmp);
            addedge(tmp,i);
            addedge(i,tmp);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        build(stu,1,n,1);
        scanf("%d",&m);
        printf("Case #%d:\n",cas);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&a,&b);
            solvesum(a,b);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值