【做题笔记】CF1528C Trees of Tranquillity

Problem

CF1528C Trees of Tranquillity

题目大意:

给你两棵 \(n\) 个点的树,构建一个由 \(n\) 个点构成的无向图 \(G\),使得 \(u,v\)\(G\) 中有边当且仅当在第一棵树中 \(u,v\) 中某一个是另一个的祖先,而在第二棵树中 \(u,v\) 中任意一个都不是另一个的祖先。

求图 \(G\) 的最大团。

Solution

若一个点集是 \(G\) 的最大团,则必须满足的条件是这些点在第一棵树上是一条链上的某些点, 且这条链的一个端点是另一个端点的祖先。

这个条件可以变得更宽一些,发现若一条链满足条件,那么把他深度较小的端点一直延伸到根上也是没关系的。

接下来考虑第二个树中的条件。转化一下其实就是选了一个点之后就不能选他子树中的点。

对于第一棵树,可以直接 dfs,并记录从他一直道根的路径上的信息,相当于要支持插点和删点操作。现在就要从中取一些点满足第二棵树的条件。
一个经典 trick 是把点转换成区间,那么相当于选出的区间不能相交或包含。

这个东西看起来好像不大可以动态插入删除?
其实观察一下发现,任意两个区间都是相离或包含,这个特殊性质非常有用。
考虑两个区间 \(a,b\),如果区间 \(a\) 包含区间 \(b\),那么选区间 \(b\) 一定比选区间 \(a\) 更优。所以可以考虑用一个 set 维护选择的区间,每次插入一个区间的时候把包含他的区间删掉,并且如果有区间被他包含就不插入。
这样解决了插入操作,删除呢?很简单,插入一个区间的时候记一下删除的区间,当删除这个区间的时候再把那个区间添加回来即可。

Code

//Think twice,code once.
#include<set>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int T,n,cnt,ans,l[300005],r[300005];
struct graph {
	int tot,hd[300005];
	int nxt[300005],to[300005];
	void clear(int n) {
		tot=0;
		for (int i=1;i<=n;i++) hd[i]=0;
		return ;
	}
	void add(int u,int v) {
		nxt[++tot]=hd[u];
		hd[u]=tot;
		to[tot]=v;
		return ;
	}
}tr1,tr2;
set<pair<int,int>>s;

void init(int now) {
	l[now]=++cnt;
	for (int i=tr2.hd[now];i;i=tr2.nxt[i]) init(tr2.to[i]);
	r[now]=cnt;
	return ;
}
void dfs(int now) {
	int flag=1;
	pair<int,int>dlt={0,0};
	auto it=s.lower_bound({l[now],r[now]});
	if (it!=s.end()&&l[now]<it->first&&it->second<=r[now]) flag=0;
	else {
		if (it!=s.begin()) {
			--it;
			if (it->first<l[now]&&r[now]<=it->second) dlt=*it,s.erase(it);
		}
		s.emplace(l[now],r[now]);
	}
	ans=max(ans,(int)s.size());
	for (int i=tr1.hd[now];i;i=tr1.nxt[i]) dfs(tr1.to[i]);
	if (flag) {
		s.erase({l[now],r[now]});
		if (dlt!=make_pair(0,0)) s.insert(dlt);
	}
	return ;
}

int main() {
	scanf("%d",&T);
	while (T--) {
		scanf("%d",&n);
		tr1.clear(n);tr2.clear(n);
		for (int i=2;i<=n;i++) {
			int fa;
			scanf("%d",&fa);
			tr1.add(fa,i);
		}
		for (int i=2;i<=n;i++) {
			int fa;
			scanf("%d",&fa);
			tr2.add(fa,i);
		}
		cnt=ans=0;
		init(1);
		dfs(1);
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2022-11-14 16:20  Mine_King  阅读(23)  评论(0编辑  收藏  举报