CF1528C dfs序+set维护

传送门

文章目录

题意:

给你两棵有 n n n个节点的树,我门记第一棵为 a a a,第二棵为 b b b,现在你有一个 n n n个点都孤立的点集,两个点 u , v u,v u,v可以连边当且仅当这两个点在 a a a树中一个是另一个的祖先节点且在 b b b树中每个都不是另一个的祖先节点,让你求连完边之后的最大联通块的大小是多少。
两棵树都以 1 1 1为根。
n ≤ 3 e 5 n\le 3e5 n3e5

思路:

首先我们知道答案选的点一定是 a a a树中从根节点到叶子节点的路径上点的一个子集,这启发我们可以从 a a a树的根开始向下 d f s dfs dfs,维护走过的点,现在我们的问题就是如何从我们维护的点中选出一个最大的点集使其在 b b b树中都是符合条件的。
b b b树中选出来的每两个点都不互为祖先,那么他们俩一定在一棵子树内,涉及子树问题我们可以将其转换成 d f s dfs dfs序。
首先需要了解 d f s dfs dfs序的性质, d f s dfs dfs序将每个子树划分为了若干区间,也就是每个子树的根有一个区间 [ l , r ] [l,r] [l,r],且划分的若干区间只会有两种情况:一个区间完全包含另一个区间,两个区间完全不相交。
现在我们对 b b b树求一个 d f s dfs dfs序,现在每个点都转换成一个区间了,对 a a a来说我们只需要需要维护一个从根到当前点的若干个不相交线段,可以分以下两个情况:
( 1 ) (1) (1)如果我们当前插入的点 u u u的区间 [ l u , r u ] [l_u,r_u] [lu,ru]包含了之前插入的区间,那么我们肯定是舍弃这个点是更优的。
( 2 ) (2) (2)如果我们当前插入的点在前面已经有的点的区间内的话,那么肯定是将前面大区间删掉,加入当前区间是更优的。
以上操作可以用 s e t set set维护一下 i n [ u ] in[u] in[u],让后 l o w e r b o u n d lower_bound lowerbound找离他最近的点的 i n [ v ] in[v] in[v],判断一下就好啦,第二种情况同理,只需要让 i t − − it-- it即可,具体的看代码比较好懂。
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

U p d a t e : Update: Update
由于题目保证了 a i < i a_i<i ai<i,那么说明标号一定是递增的,所以不会出现当前区间是大区间,之前有小区间的情况,所以 i t = = s . e n d ( ) ∣ ∣ ∗ i t > o u t [ u ] it==s.end()||*it>out[u] it==s.end()it>out[u]这个判断条件可以直接去掉。

// Problem: E. Trees of Tranquillity
// Contest: Codeforces - Codeforces Round #722 (Div. 2)
// URL: https://codeforces.com/contest/1529/problem/E
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;

//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

const int N=1000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;

int n;
int in[N],out[N],id[N],tot,ans;
set<int>s;
vector<int>v1[N],v2[N];

void dfs2(int u) {
	in[u]=++tot; id[tot]=u;
	for(auto x:v2[u]) dfs2(x);
	out[u]=tot;
}

void dfs1(int u) {
	int flag=false;
	int del=-1;
	auto it=s.lower_bound(in[u]);
	if(it==s.end()||*it>out[u]) {
		if(it!=s.begin()) {
			it--;
			if(out[id[*it]]>=in[u]) {
				del=*it;
				s.erase(it);
			}
		}
		s.insert(in[u]);
		flag=true;
	}
	ans=max(ans,(int)s.size());
	for(auto x:v1[u]) dfs1(x);
	if(flag) {
		s.erase(in[u]);
		if(del!=-1) s.insert(del);
	}
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);
	
	int _; scanf("%d",&_);
	while(_--) {
		scanf("%d",&n);
		tot=0; s.clear(); ans=0;
		for(int i=1;i<=n;i++) v1[i].clear(),v2[i].clear();
		for(int i=2;i<=n;i++) {
			int x; scanf("%d",&x);
			v1[x].pb(i);
		}
		for(int i=2;i<=n;i++) {
			int x; scanf("%d",&x);
			v2[x].pb(i);
		}
		dfs2(1); dfs1(1);
		printf("%d\n",ans);
	}



	return 0;
}
/*

*/









  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值