CodeForces - 1529E Trees of Tranquillity(贪心+线段树)

105 篇文章 4 订阅
93 篇文章 2 订阅

题目链接:https://vjudge.net/problem/CodeForces-1529E

题目大意:给出两棵根节点为 1 1 1 的树,分别称为 A A A 树和 B B B 树,现在通过两棵树可以构造出一个无向图,当且仅当点对 ( x , y ) (x,y) (x,y) 同时满足以下两个条件时,可以在图中建边:

  1. A A A 树中, x x x y y y 的祖先或 y y y x x x 的祖先
  2. B B B 树中, x x x 不能是 y y y 的祖先同时 y y y 不能是 x x x 的祖先

求该图的最大团

题目分析:一开始读错题了,后来读对题后分析的差不多时间也到点了,今天补题的时候发现和赛场的思路差的不算太多,就是一个挺简单的贪心

思考一下,满足最大团的点,在 A A A 树上和 B B B 树上的形态是什么样的,首先这些点在 A A A 树上一定是一条,从根节点到叶子结点的链上的点,可以不连续。在 B B B 树上就可以体现为,所有点的 d f s dfs dfs 序都没有交集

继续分析,所有点的 d f s dfs dfs 序没有交集,也就是说需要选择尽可能多的区间,使其互不相交,这样就将树上的问题转换成线性问题了

而在 A A A 树上需要满足所有的点在同一条链上,在 d f s dfs dfs 的时候贪心选择尽可能多的点就可以了

然后是题目针对于本题输入的一个特殊性质,基于给出的树的输入方式,是每次给出点 i i i 的父节点。这也就使得, A A A 树和 B B B 树从根节点拉下来的一条链,一定是一个单调递增的序列,结合于此,针对于 B B B 树的 d f s dfs dfs 序的区间,有一个下面会用到的性质:

序号较小的点的区间,要么包含序号较大的点的区间,要么与其不相交

接下来就可以对 A A A d f s dfs dfs 去贪心了,需要维护一个数据结构,需要实现以下功能:

  1. 插入一个区间
  2. 删除一个区间
  3. 判断存在的区间是否存在交集

可以用平衡树(set),也可以用线段树,我觉得用线段树比较好理解,就选择了线段树实现

当遍历到点 x x x 时,尝试将点 x x x 所代表的区间插入到线段树中,无非会出现三种情况:

  1. [ L [ x ] , R [ x ] ] [L[x],R[x]] [L[x],R[x]] 中原先并没有区间,则直接插入即可
  2. [ L [ x ] , R [ x ] ] [L[x],R[x]] [L[x],R[x]] 被一个更大的区间覆盖,则将其删掉,并替换上点 x x x 所代表的区间显然更优
  3. [ L [ x ] , R [ x ] ] [L[x],R[x]] [L[x],R[x]] 中包含了大于一个区间,此时如果想加入 x x x 所代表的区间,就需要将冲突的区间全都删掉,显然舍弃掉 x x x 所代表的区间是更优的,实际操作就是,无需操作

回溯的时候将 x x x 所代表的区间按照上述三种情况还原即可

最后的最后还有一个比较难解决的小问题,就是,如何快速查询区间 [ L [ x ] , R [ x ] ] [L[x],R[x]] [L[x],R[x]] 中存在的不同区间的个数,以及如何快速更新线段树

还记得前面加粗的性质吗?序号大小这个条件还没有用上呢

其实我们可以直接去查询 [ L [ x ] , R [ x ] ] [L[x],R[x]] [L[x],R[x]] 中,存在的区间的最大编号即可,因为在 A A A 树上 d f s dfs dfs 遍历的链也是递增遍历的,结合加粗的性质,如果 [ L [ x ] , R [ x ] ] [L[x],R[x]] [L[x],R[x]] 之前被覆盖过,那么一定是一个更大的区间覆盖的,所以上述的三种情况里,第三种情况实际上根本不可能出现

代码:

// 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(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
#include<unordered_map>
#define lowbit(x) x&-x
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)
{
	T f=1;x=0;
	char ch=getchar();
	while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	x*=f;
}
template<typename T>
inline void write(T x)
{
	if(x<0){x=~(x-1);putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
const int inf=0x3f3f3f3f;
const int N=1e6+100;
vector<int>a[N],b[N];
int L[N],R[N],dfn,sum,ans;
struct Node {
	int l,r,mmax,lazy;
}tree[N<<2];
void pushup(int k) {
	tree[k].mmax=max(tree[k<<1].mmax,tree[k<<1|1].mmax);
}
void pushdown(int k) {
	if(tree[k].lazy!=-1) {
		int lz=tree[k].lazy;
		tree[k].lazy=-1;
		tree[k<<1].mmax=tree[k<<1|1].mmax=lz;
		tree[k<<1].lazy=tree[k<<1|1].lazy=lz;
	}
}
void build(int k,int l,int r) {
	tree[k]={l,r,0,-1};
	if(l==r) {
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}
void update(int k,int l,int r,int val) {
	if(tree[k].l>r||tree[k].r<l) {
		return;
	}
	if(tree[k].l>=l&&tree[k].r<=r) {
		tree[k].mmax=tree[k].lazy=val;
		return;
	}
	pushdown(k);
	update(k<<1,l,r,val);
	update(k<<1|1,l,r,val);
	pushup(k);
}
int query(int k,int l,int r) {
	if(tree[k].l>r||tree[k].r<l) {
		return 0;
	}
	if(tree[k].l>=l&&tree[k].r<=r) {
		return tree[k].mmax;
	}
	pushdown(k);
	return max(query(k<<1,l,r),query(k<<1|1,l,r));
}
void dfs1(int u) {
	L[u]=++dfn;
	for(auto v:b[u]) {
		dfs1(v);
	}
	R[u]=dfn;
}
void dfs2(int u) {
	int mmax=query(1,L[u],R[u]);
	if(!mmax) {
		update(1,L[u],R[u],u);
		sum++;
	} else {
		update(1,L[mmax],R[mmax],0);
		update(1,L[u],R[u],u);
	}
	ans=max(ans,sum);
	for(auto v:a[u]) {
		dfs2(v);
	}
	if(!mmax) {
		update(1,L[u],R[u],0);
		sum--;
	} else {
		update(1,L[u],R[u],0);
		update(1,L[mmax],R[mmax],mmax);
	}
}
int main()
{
#ifndef ONLINE_JUDGE
//	freopen("data.in.txt","r",stdin);
//	freopen("data.out.txt","w",stdout);
#endif
//	ios::sync_with_stdio(false);
	int w;
	cin>>w;
	while(w--) {
		int n;
		read(n);
		dfn=0;
		for(int i=1;i<=n;i++) {
			a[i].clear();
			b[i].clear();
		}
		for(int i=2;i<=n;i++) {
			int fa;
			read(fa);
			a[fa].push_back(i);
		}
		for(int i=2;i<=n;i++) {
			int fa;
			read(fa);
			b[fa].push_back(i);
		}
		dfn=ans=sum=0;
		build(1,1,n);
		dfs1(1);
		dfs2(1);
		cout<<ans<<endl;
	}
	return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Frozen_Guardian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值