Loading

VP - Codeforces Round 722 Div.2

这场比赛我只打了一个小时,赛时通过 \(\text{A,B,C}\),排名 \(880\)(算上 Unofficial)。

A

略。

B

略。

C

显然让每个点的数都取它的边界是最优的,然后 dp 即可。

D

考虑点 \(1\) 的配对,设其与 \(x\) 构成一条线段。

\(f_i\)\(2i\) 个点自由配对的方案数。

  • \(x\le n\):因为从 \(1\) 开始的线段不可能被其它线段覆盖,所以 \(1\sim 2n\) 一定铺满了长度为 \((x-1)\) 的线段。因为要恰好铺满,所以方案数为 \(\sigma_0(n)\)
  • \(x>n\):此时 \(x\sim 2n\) 这些点一定会与 \(1\sim (2n-x+1)\) 这些点对应地配对。因此还有 \(2(x-n-1)\) 个点可以随意配对,方案数即为 \(\sum_{i=1}^{n-1} f_i\)

E

设 Soroush 的树是 \(S\),Keshi 的树是 \(T\)

首先,最大团中的点一定都在 \(S\) 中从 \(1\) 到某个叶子节点的路径上。并且可以发现,在路径确定时,最优的方案是优先选在 \(T\) 中深度较浅的点。

也就是说,我们要有一种算法,支持如下操作:

  • 加入一个点;
  • 删除一个点,且这个点被删除之前,其在 \(S\) 中的所有子孙都被删除了;
  • 询问目前点集中最多能取出多少个点,使得它们在 \(T\) 中两两无祖先关系。

设操作三的点集是 \(P\),那么对于操作一,设加入了点 \(u\),假如 \(u\)\(P\) 中某个点的祖先,那么在 \(P\) 中加入 \(u\) 显然不优;否则将 \(P\)\(u\) 的祖先删除并加入 \(u\)

如何查询 \(P\) 中是否有 \(u\) 的祖先?考虑预处理 \(T\) 的欧拉序,记为 \((st_u,ed_u)\),并维护 \(P\) 中所有点的从小到大排序的 \(st\)。因为要查的是 \(u\) 的祖先(记为 \(x\)),所以一定有 \(st_x\le st_u\land ed_x\ge ed_u\)。找到 \(P\) 中最后一个满足 \(st_x\le st_u\)\(x\),可以证明,如果 \(P\) 中存在 \(u\) 的祖先,那么它一定是 \(x\)

证明

假如 \(P\) 中存在某个点 \(v(v\neq x)\) 使得 \(v\)\(u\) 的祖先,那么有 \(st_v<st_x\)。讨论 \(x\) 的两种可能:

  • 如果 \(x\)\(u\) 的祖先,那么显然得证;
  • 如果 \(x\) 不是 \(u\) 的祖先,那么有 \(ed_x<ed_u\)。因为 \(v\)\(u\) 的祖先,所以 \(st_v<st_x\land ed_v>ed_x\),即 \(v\) 也是 \(x\) 的祖先,与 \(P\) 的定义矛盾。

查询 \(u\) 是否是 \(P\) 中某个点的祖先:找到 \(P\) 中第一个满足 \(st_v>st_u\)\(v\)。因为欧拉序有一个性质:两个点代表的区间要不然一个包含一个,要不然不相交,所以只需要判断 \(ed_v\) 是否小于 \(ed_u\) 即可。

对于删除操作,每次添加操作修改的点个数是 \(O(1)\) 的,因此在 \(S\) 中每个点上记录修改即可。

代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <set>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &x){
	x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	x=x*_f;
}
template<typename T,typename... Args> void Read(T &x,Args& ...others){
	Read(x);Read(others...);
}
typedef long long ll;
const int Inf=0x3f3f3f3f,N=3e5+5;
int Kase,n;
vector<int> S[N],T[N];
int st[N],ed[N],dfx=0;
void Dfs(int u){
	st[u]=++dfx;
	for(int v:T[u]){Dfs(v);}
	ed[u]=++dfx;
}
struct Clique{
	struct Cmp{
		bool operator()(int x,int y) const{return st[x]<st[y];}
	};
	set<int,Cmp> clq;
	bool SonOf(int u){
		auto it=clq.upper_bound(u);
		if(it==clq.end()) return 0;
		return ed[*it]<ed[u];
	}
	int AncOf(int u){
		auto it=clq.upper_bound(u);
		if(it==clq.begin()) return 0;
		int v=*--it;
		return (st[v]<st[u]&&ed[u]<ed[v])?v:0;
	}
	void Add(int u,int &add,int &del){
		if(SonOf(u)) return;
		int v=AncOf(u);
		if(v) clq.erase(v),del=v;
		clq.insert(u),add=u;
	}
}c;
int ans=0;
void Work(int u){
	int ad=0,dl=0;
	c.Add(u,ad,dl);ans=max(ans,static_cast<int>(c.clq.size()));
	for(int v:S[u]) Work(v);
	if(ad) c.clq.erase(ad);
	if(dl) c.clq.insert(dl);
}
int main(){
	Read(Kase);
	while(Kase--){
		ans=dfx=0;c.clq.clear();
		Read(n);
		For(i,1,n) S[i].clear(),T[i].clear();
		int x;
		For(i,2,n){
			Read(x);S[x].push_back(i);
		}
		For(i,2,n){
			Read(x);T[x].push_back(i);
		}
		Dfs(1);
		Work(1);
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2021-06-26 21:12  Alan_Zhao_2007  阅读(42)  评论(0编辑  收藏  举报