hdu_4718_The LCIS on the Tree(树链剖分+线段树合并)

15 篇文章 0 订阅
4 篇文章 0 订阅

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4718

题意:给你一棵树,每个节点有一个值,然后任给树上的两点,问这两点的最长连续递增区间是多少

题解:先树链剖分,然后结合线段树的区间合并来搞,注意的是要记录递增和递减两个状态,因为线段树的区间都是从根到子节点,如果询问从子节点到子节点,那么就是一增一减

#include<cstdio>
#include<algorithm>
#define F(i,a,b) for(int i=a;i<=b;i++)
#define root 1,n,1
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
const int N=1e5+7;
int t,x,y,n,ic=1,q,cnt,g[N],ed,nxt[N],v[N],a[N];
inline void adg(int x,int y){v[++ed]=y,nxt[ed]=g[x],g[x]=ed;}
//树链剖分----------------------------
int dep[N],sz[N],fa[N],hs[N],tid[N],top[N],idx,cot[N];
void dfs1(int u,int pre){
	dep[u]=dep[pre]+1,sz[u]=1,fa[u]=pre,hs[u]=0;
	for(int i=g[u];~i;i=nxt[i]){
		dfs1(v[i],u),sz[u]+=sz[v[i]];
		if(sz[v[i]]>sz[hs[u]])hs[u]=v[i];
	}
}
void dfs2(int u,int tp){
	top[u]=tp,tid[u]=++idx,cot[tid[u]]=a[u];
	if(hs[u])dfs2(hs[u],tp);
	for(int i=g[u];~i;i=nxt[i])if(v[i]!=hs[u])dfs2(v[i],v[i]);
}
//线段树-------------------------------
int Rn[N<<3],Ln[N<<3],len[N<<3],ml[N<<3],Rl[N<<3];
int Ll[N<<3],dml[N<<3],dRl[N<<3],dLl[N<<3];

inline void up(int rt,int li,int ri){
	Ln[rt]=Ln[li],Rn[rt]=Rn[ri];
	Ll[rt]=Ll[li],Rl[rt]=Rl[ri];
	dLl[rt]=dLl[li],dRl[rt]=dRl[ri];
	ml[rt]=max(ml[li],ml[ri]);
	dml[rt]=max(dml[li],dml[ri]);
	if(Rn[li]<Ln[ri]){//左右区间可以合并
		ml[rt]=max(ml[rt],Rl[li]+Ll[ri]);
		if(Rl[li]==len[li])Ll[rt]=Ll[li]+Ll[ri];
		if(Rl[ri]==len[ri])Rl[rt]=Rl[li]+Rl[ri];
	}
	if(Rn[li]>Ln[ri]){
		dml[rt]=max(dml[rt],dRl[li]+dLl[ri]);
		if(dRl[li]==len[li])dLl[rt]=dLl[li]+dLl[ri];
		if(dRl[ri]==len[ri])dRl[rt]=dRl[li]+dRl[ri];
	}
}

void build(int l,int r,int rt){
	len[rt]=r-l+1;
	if(l==r){
		Ln[rt]=Rn[rt]=cot[l],ml[rt]=Ll[rt]=Rl[rt]=1;
		dml[rt]=dLl[rt]=dRl[rt]=1;
		return;
	}
	int m=(l+r)>>1;
	build(ls),build(rs),up(rt,rt<<1,rt<<1|1);
}
//将两个区间合并
inline void adt(int li,int ri,int rt){len[rt]=len[li]+len[ri],up(rt,li,ri);}

int anson(int l,int r){
	int ans=max(dml[l],ml[r]);
	if(Ln[l]<Ln[r])return max(ans,dLl[l]+Ll[r]);
	return ans;
}

int query(int L,int R,int l,int r,int rt){
	if(L==l&&R==r)return rt;
	int m=(l+r)>>1;
	if(m>=R)return query(L,R,ls);
	else if(m<L)return query(L,R,rs);
	else{
		int lss=query(L,m,ls),rss=query(m+1,R,rs);
		adt(lss,rss,++cnt);
		return cnt;
	}
}

int lca(int x,int y){
	if(x==y)return 1;
	cnt=N<<2;
	int xp=-1,yp=-1,op;
	while(top[x]!=top[y]){
		if(dep[top[x]]>dep[top[y]]){
			op=query(tid[top[x]],tid[x],root),x=fa[top[x]];
			if(xp==-1)xp=op;
			else adt(op,xp,++cnt),xp=cnt;
		}else{
			op=query(tid[top[y]],tid[y],root),y=fa[top[y]];
			if(yp==-1)yp=op;
			else adt(op,yp,++cnt),yp=cnt;
		}
	}
	if(dep[x]>=dep[y]){
		op=query(tid[y],tid[x],root);
		if(xp==-1)xp=op;
		else adt(op,xp,++cnt),xp=cnt;
	}else{
		op=query(tid[x],tid[y],root);
		if(yp==-1)yp=op;
		else adt(op,yp,++cnt),yp=cnt;
	}
	if(xp==-1)return ml[yp];
	if(yp==-1)return dml[xp];
	return anson(xp,yp);
}

int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		F(i,0,N-1)g[i]=-1;ed=0;
		F(i,1,n)scanf("%d",a+i);
		F(i,2,n)scanf("%d",&x),adg(x,i);
		dfs1(1,0),idx=0,dfs2(1,1),build(root);
		scanf("%d",&q);
		printf("Case #%d:\n",ic++);
		while(q--)scanf("%d%d",&x,&y),printf("%d\n",lca(x,y));
		if(t)puts("");
	}
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值