|
The LCIS on the Tree Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others) 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).
Input
The first line has a number T (T <= 10) , indicating the number of test cases.
Output
For test case X, output "Case #X:" at the first line.
Sample Input
Sample Output
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);
}
}
}