【2016北京集训】银河战舰

Portal --> broken qwq

Description

  给你一棵树,每个节点有一个权值,求树上最长的一条链上的最长上升子序列长度

  数据范围:\(2<=n<=200000,1<=val<=10^6\),其中\(val\)表示点权

  

Solution

  深陷点分无法自拔论我的头里面都装了些什么== 大家好我是一个失去脑子的点分+线段树选手==

​  这题的话。。首先一个前置技能是nlogn子序列,然后我们只要把这个玩意搬到树上就好了

​  注意到假设答案过一个点\(x\),那么肯定是\(x\)的某个子树中的一段上升子序列+另一个子树中的一段下降子序列,所以我们需要维护两个东西

​  类似于序列上的求解,我们对于树上的每一个节点维护一个\(up\)数组和\(dw\)数组,\(up[i]\)表示该节点子树中长度为\(i\)的上升子序列(从深到浅)的深度最浅的那个值最小是多少,\(dw[i]\)类似地表示下降子序列深度最浅的那个值最大是多少

​  那么答案的求解应该就是对于每个点选择其两个不同的后继子树,其中一个取\(up\),另一个取\(dw\),拼在一起,然后还要分为取这个点或者不取这个点两种情况

​  然后对于一个节点的\(up\)\(dw\)就是直接从子树中继承过来就好了,最后再加上单独自己这个点的情况

​  现在的问题是,我们要怎么实现

​  对于求解\(ans\)中那个“选择两个不同的子树”这个,我们可以用先求解再更新的套路来处理,每次dfs完一个儿子,我们先用这个儿子的信息和当前的合并了前面遍历的所有儿子信息的数组(汇总数组)进行答案求解,然后再将这个儿子的信息合并到那个汇总数组中

  合并信息的话,注意到合并两个信息数组的复杂度和该数组对应的子树深度有关,所以我们考虑启发式合并的思想,直接暴力将小的往大的里面插就好了

​  这种思想也会用在求解中,我们考虑暴力枚举较小的那个数组中的每个元素,然后在另一个数组中二分找到可以接上的最长长度,然后更新答案即可

​  最后是加入自己单独这个点:这部分的处理其实和普通的序列问题处理方式基本一致,也是二分一波然后直接更新就好了

​  再具体一点的话。。可以考虑用vector来存每个节点的\(up\)\(dw\),不过这样需要注意一点就是下标和其对应的长度会差\(1\),这个偏移在计算的时候需要注意

  虽然不太会证明。。但是跟启发式合并类似,总的时间复杂度是\(O(nlogn)\)

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=2*(1e5)+10;
struct xxx{
	int y,nxt;
}a[N*2];
vector<int> up[N],dw[N];
vector<int>::iterator it;
int h[N],mxdep[N],son[N],dep[N];
int val[N];
int n,m,tot,ans;
bool cmp(int x,int y){return x>y;}
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void merge(int x,int y){//into x
	int szx=up[x].size(),szy=up[y].size();
	if (szx>=szy)
		for (int i=0;i<szy;++i)
			up[x][i]=min(up[x][i],up[y][i]);
	else{
		for (int i=0;i<szx;++i)
			up[y][i]=min(up[y][i],up[x][i]);
		swap(up[x],up[y]);
	}

	szx=dw[x].size(); szy=dw[y].size();
	if (szx>=szy)
		for (int i=0;i<szy;++i)
			dw[x][i]=max(dw[x][i],dw[y][i]);
	else{
		for (int i=0;i<szx;++i)
			dw[y][i]=max(dw[y][i],dw[x][i]);
		swap(dw[x],dw[y]);
	}
}
void choose(int x,int y,int who){
	int len1=lower_bound(up[x].begin(),up[x].end(),val[who])-up[x].begin();
	int len2=lower_bound(dw[y].begin(),dw[y].end(),val[who],cmp)-dw[y].begin();
	ans=max(ans,len1+len2+1);
}
void nchoose(int x,int y,int who){
	int len,szx=up[x].size(),szy=dw[y].size();
	if (szx>=szy){
		for (int i=0;i<szy;++i){
			len=lower_bound(up[x].begin(),up[x].end(),dw[y][i])-up[x].begin();
			ans=max(ans,(i+1)+len);
		}
	}
	else{
		for (int i=0;i<szx;++i){
			len=lower_bound(dw[y].begin(),dw[y].end(),up[x][i],cmp)-dw[y].begin();
			ans=max(ans,(i+1)+len);
		}
	}
}
void get_ans(int x,int u){
	int len1,len2;
	choose(x,u,x);
	choose(u,x,x);

	nchoose(x,u,x);
	nchoose(u,x,x);
}
void insert(int x){
	int pos,sz=up[x].size();
	it=upper_bound(up[x].begin(),up[x].end(),val[x]);
	pos=it-up[x].begin();
	if (it==up[x].end()){
		if (!sz||up[x][sz-1]<val[x])
			up[x].push_back(val[x]);
	}
	else if (pos==0||up[x][pos-1]<val[x])
		up[x][pos]=val[x];

	sz=dw[x].size();
	it=upper_bound(dw[x].begin(),dw[x].end(),val[x],cmp);
	pos=it-dw[x].begin();
	if (it==dw[x].end()){
		if (!sz||dw[x][sz-1]>val[x])
			dw[x].push_back(val[x]);
	}
	else if (pos==0||dw[x][pos-1]>val[x])
		dw[x][pos]=val[x];
}
void dfs(int fa,int x){
	int u;
	for (int i=h[x];i!=-1;i=a[i].nxt){
		u=a[i].y;
		if (u==fa) continue;
		dfs(x,u);
		get_ans(x,u);
		merge(x,u);
	}
	insert(x);
	int debug=1;
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	int x,y;
	scanf("%d",&n);
	memset(h,-1,sizeof(h));
	tot=0;
	for (int i=1;i<=n;++i) scanf("%d",val+i);
	for (int i=1;i<n;++i){
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	ans=0;
	dfs(0,1);
	printf("%d\n",ans);
}
posted @ 2018-09-28 21:04  yoyoball  阅读(129)  评论(0编辑  收藏  举报