nfls20034 12449 - 上升

pb 出的毒瘤场,也就这题比较可做了(还是在 hsc 提示思路下),所以单独写个题解。


nmd 老子上午和下午全部在调这题(包括线段树合并的做法)。

首先可以感性地发现这个题不弱于 CF490F 树上全局 LIS。一个证明:我们可以将原树复制一遍,中间用一条边连接,这样无论删除哪个点都会剩下一棵完整的原树,而删除连接的两端之后跑该题应该恰好等于原树的全局 LIS,这样就把全局 LIS 归约到了这题。

一个直接的思路是换根。使用 CF490F 的线段树合并做法可以在固定根的时候求出每个子树内的 LIS,这样如果换根成功的话,可以把根的所有儿子子树取个 max。但是很可惜,线段树合并复杂度是均摊的,换根会破坏复杂度。(包括下面要说的长剖做法,更不行了,因为长剖是建立在有根的基础上的。。。)

但是可以发现:任意找到一条 LIS,如果删除 LIS 所在极小链以外的节点,那么不会破坏该 LIS,删除之后的连通块的最大 LIS 显然依然等于原来的 LIS。所以我们只需要任意找一条 LIS 所在链,求出以上面所有节点为根时的答案即可,稍后我们将看到这是容易的。这个思路就很妙啊,跟《God 的高妙算法》那题异曲同工,树上解决不了的问题,就先用一个简单的步骤将候选答案集合筛成一条链,进而转化为序列问题。

我们选 \(1\) 为根时,删除一个点后,只有它父亲所在连通块还没被计算出(其它都是子树),想要换根其实就是想要解决这个问题。现在只要求链上所有节点答案的话,不难发现以链的两端为根时,对每个链上的点至少会有一种情形使得它在 \(1\) 为根时父亲所在连通块为当前的儿子树。那么就先以 \(1\) 为根跑一遍全局 LIS,然后以两端为根,这样总共跑三遍即可。而且 DP 时还要记录具体的路径端点,这就要套一堆 pair,讨论也烦很多,异常难写。然后 5e5 2s,跑三遍线段树合并还套 pair 就非常卡常好吧,我尽了所有努力最终只能卡到 2.8s,所以不得不换个常数小的做法。

观察 CF490F 的题解区发现一个长剖 + 二分的小常数做法,现在来搬运一波。之前是设 DP 状态为 \(x\) 子树内从 \(x\) 往下的直 LIS / LDS,考虑交换一下定义域和值域:设 \(dp_{x,d}\) 为子树 \(x\) 内长度为 \(d\) 的直 LIS / LDS 的最上面的最大值 / 最小值。由于 \(dp_x\) 数组长度与子树 \(x\) 深度成线性,考虑长剖优化。继承深儿子的话,这回是 push_back 而非 push_front,不需要 deque,直接 vector 即可(开 O2 下并不太需要担心 vector 常数,所以也不用写繁琐的指针写法,继承直接用 swap 函数是 \(\mathrm O(1)\) 的)。转移的话,合并的时候直接往上 max 即可。更新答案的话,还是跟线段树合并一样分三类,容易发现 \(dp_x\)​ 数组一定是单调的,所以可以直接在被合并数组上二分。枚举!!二分!!!多好写啊。然而还是有很多细节。虽然二分的常数乘以 6,但还是不需要刻意卡常就过了。

「铁」个毒瘤代码:
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define X first
#define Y second
#define pb push_back
const int inf=0x3f3f3f3f;
const int N=5e5+10;
int n;
vector<int> nei[N];
int a[N];
int mxdep[N],dson[N];
void dfs1(int x,int fa=0){
	mxdep[x]=1;
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(y==fa)continue;
		dfs1(y,x);
		mxdep[x]=max(mxdep[x],mxdep[y]+1);
		if(mxdep[y]>mxdep[dson[x]])dson[x]=y;
		assert(mxdep[y]>0);
	}
}
pair<int,pair<int,int> > ans[N],tot[N];
vector<pair<int,int> > dp_i[N],dp_d[N];
int rl[N];
void dfs2(int x,int fa=0){
	ans[x]=tot[x]=mp(0,mp(0,0));
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(y==fa||y==dson[x])continue;
		dfs2(y,x);
		tot[x]=max(tot[x],tot[y]),rl[x]=max(rl[x],tot[y].X);
	}
	if(dson[x]){
		dfs2(dson[x],x),dp_i[x].swap(dp_i[dson[x]]),dp_d[x].swap(dp_d[dson[x]]);
		tot[x]=max(tot[x],tot[dson[x]]),rl[x]=max(rl[x],tot[dson[x]].X);
	}
	else dp_i[x].resize(1,mp(inf,x)),dp_d[x].resize(1,mp(0,x));
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(y==fa||y==dson[x])continue;
		for(int j=1;j<=mxdep[y];j++){
//			assert(dp_i[y][j].X<inf);assert(dp_d[y][j].X);
			vector<pair<int,int> >::iterator fd;
			if(dp_i[y][j].X>a[x]){
				fd=lower_bound(dp_d[x].begin()+1,dp_d[x].end(),mp(a[x],0));
				if(fd!=dp_d[x].begin()+1)ans[x]=max(ans[x],mp(int(fd-dp_d[x].begin()+j),mp(dp_i[y][j].Y,(fd-1)->Y)));
			}
			if(dp_d[y][j].X<a[x]){
				fd=lower_bound(dp_i[x].begin()+1,dp_i[x].end(),mp(a[x],inf),greater<pair<int,int> >());
				if(fd!=dp_i[x].begin()+1)ans[x]=max(ans[x],mp(int(fd-dp_i[x].begin()+j),mp(dp_d[y][j].Y,(fd-1)->Y)));
			}
			if(dp_i[y][j].X){
				fd=lower_bound(dp_d[x].begin()+1,dp_d[x].end(),mp(dp_i[y][j].X,0));
//				if(!(fd-1)->Y)cout<<fd-dp_d[x].begin()-1<<"!\n";
				if(fd!=dp_d[x].begin()+1)ans[x]=max(ans[x],mp(int(fd-1-dp_d[x].begin()+j),mp(dp_i[y][j].Y,(fd-1)->Y)));
			}
			if(dp_d[y][j].X<inf){
				fd=lower_bound(dp_i[x].begin()+1,dp_i[x].end(),mp(dp_d[y][j].X,inf),greater<pair<int,int> >());
				if(fd!=dp_i[x].begin()+1)ans[x]=max(ans[x],mp(int(fd-1-dp_i[x].begin()+j),mp(dp_d[y][j].Y,(fd-1)->Y)));
			}
		}
		for(int j=1;j<=mxdep[y];j++)dp_i[x][j]=max(dp_i[x][j],dp_i[y][j]),dp_d[x][j]=min(dp_d[x][j],dp_d[y][j]);
	}
	dp_d[x].pb(mp(inf,0)),dp_i[x].pb(mp(0,0));
	vector<pair<int,int> >::iterator fd;
	fd=lower_bound(dp_d[x].begin(),dp_d[x].end(),mp(a[x],0));
	ans[x]=max(ans[x],mp(int(fd-dp_d[x].begin()),mp(x,(fd-1)->Y))),*fd=mp(a[x],(fd-1)->Y);
	fd=lower_bound(dp_i[x].begin(),dp_i[x].end(),mp(a[x],inf),greater<pair<int,int> >());
	ans[x]=max(ans[x],mp(int(fd-dp_i[x].begin()),mp(x,(fd-1)->Y))),*fd=mp(a[x],(fd-1)->Y);
	tot[x]=max(tot[x],ans[x]);
//	for(int i=1;i<=mxdep[x];i++)assert(dp_d[x][i].X==inf||dp_d[x][i].Y);
//	cout<<x<<" "<<ans[x].X<<"!\n";
//	cout<<x<<" 's i: ";for(int i=1;i<=mxdep[x];i++)cout<<dp_i[x][i].X<<","<<dp_i[x][i].Y<<" ";puts("");
//	cout<<x<<" 's d: ";for(int i=1;i<=mxdep[x];i++)cout<<dp_d[x][i].X<<","<<dp_d[x][i].Y<<" ";puts("");
}
void deal(int x){
	assert(x);
	memset(dson,0,sizeof(dson));for(int i=1;i<=n;i++)dp_i[i].clear(),dp_d[i].clear();
	dfs1(x);
	dfs2(x);
}
vector<int> v;
bool upd[N];
void dfs0(int x,int aim,int fa=0){
	v.pb(x);
	if(x==aim)for(int i=0;i<v.size();i++)upd[v[i]]=true;
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(y==fa)continue;
		dfs0(y,aim,x);
	}
	v.pop_back();
}
int main(){
	freopen("lis.in","r",stdin);freopen("lis.out","w",stdout);
	cin>>n;
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		nei[x].pb(y),nei[y].pb(x);
	}
	for(int i=1;i<=n;i++)scanf("%d",a+i);
	deal(1);
	pair<int,pair<int,int> > mx=tot[1];
	int x=mx.Y.X,y=mx.Y.Y;
//	cout<<x<<" "<<y<<" "<<mx.X<<"!\n";
	dfs0(x,y);
	deal(x),deal(y);
	int out=inf;
	for(int i=1;i<=n;i++)if(upd[i])out=min(out,rl[i]);
	cout<<out<<"\n";
	return 0;
}
posted @ 2021-09-28 20:03  ycx060617  阅读(54)  评论(1编辑  收藏  举报