E.Trees of Tranquillity

4 篇文章 0 订阅
3 篇文章 0 订阅

E. Trees of Tranquillity
题意:
多组输入,给两个都是n个节点的树A和树B
然后给一个n个节点的图,然后如果两个点x和y同时满足以下两个条件则连边,
1.在树A中x是y的祖先或者y是x的祖先,2,在树B中x和y谁也不是谁的祖先
求图的最大团的大小
n<=3e5
输入的树边为a2到an,ai代表i的父亲是ai,1<=ai<i
思路:
A树上的肯定在一条链上,B树则为dfs序的不重叠区间
这个题的巧妙之处在于输入ai<i这个限制,这样一条链上的各个点标号一定是递增的。
然后dfsA树贪心即可,如果当前这个点在B树上对应的dfs序区间被覆盖过了,那么直接贪心去掉之前的,用当前这个点的区间代替即可,这样贡献不变,覆盖的区间变小了。B树上因为标号大的区间一定不会覆盖标号小的区间这一性质,所以可以这样贪心。
这个题ai<i很关键,之前没碰到过这种题,其他的思维部分也挺锻炼思维能力的。

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int MAX_N=301000;
struct node{
	int l,r,maxl,lazy;
}a[MAX_N*4];
void update(int k){
 	a[k].maxl=max(a[k<<1].maxl,a[k<<1|1].maxl);
}
void build(int k,int l,int r){
 	a[k].l=l;a[k].r=r;a[k].lazy=-1;
 	if(l==r){
  		a[k].maxl=0;
  		return;
 	}
 	int mid=(l+r)>>1;
 	build(k<<1,l,mid);
 	build(k<<1|1,mid+1,r);
 	update(k);
}
void pushdown(int k){
 	if(a[k].lazy==-1)
 	return;
 	a[k<<1].lazy=a[k<<1|1].lazy=a[k].lazy;
 	a[k<<1].maxl=a[k<<1|1].maxl=a[k].lazy;
 	a[k].lazy=-1;
}
void changeS(int k,int l,int r,int y){
 	if(a[k].l>=l&&a[k].r<=r){
  		a[k].maxl=y;
  		a[k].lazy=y;
  		return;
 	}
 	pushdown(k);
 	int mid=(a[k].l+a[k].r)>>1;
 	if(l<=mid)
 	changeS(k<<1,l,r,y);
 	if(r>mid)
 	changeS(k<<1|1,l,r,y);
 	update(k);
}
int find(int k,int x){
 	if(a[k].l==a[k].r){
  		return a[k].maxl;
 	}
 	pushdown(k);
 	int mid=(a[k].l+a[k].r)>>1;
 	if(x<=mid)
 	return find(k<<1,x);
 	else
 	return find(k<<1|1,x);
}
int b[MAX_N],c[MAX_N];
vector<int>v1[MAX_N],v2[MAX_N];
int l[MAX_N],r[MAX_N];
int tot=0;
void dfs2(int x,int fa){
	int i;
	l[x]=++tot;
	for(i=0;i<v2[x].size();i++){
		int y=v2[x][i];
		if(y==fa)
		continue;
		dfs2(y,x);
	}
	r[x]=tot;
}
int ans=0;
void dfs1(int x,int fa,int val){
	int i;
	int res=val;
	int p=find(1,l[x]);
	//cout<<x<<" "<<val<<" "<<p<<" !!!\n";
	//cout<<l[x]<<" "<<r[x]<<" ??\n";
	if(p){
		changeS(1,l[p],r[p],0);
		changeS(1,l[x],r[x],x);
	}
	else{
		changeS(1,l[x],r[x],x);
		res++;	
	}
	ans=max(ans,res);
	for(i=0;i<v1[x].size();i++){
		int y=v1[x][i];
		if(y==fa)
		continue;
		dfs1(y,x,res);
	}
	if(p){
		changeS(1,l[p],r[p],p);
	}
	else{
		changeS(1,l[x],r[x],0);
	}
}
int main(void){
	int T,n,i;
	cin>>T;
	while(T--){
		scanf("%d",&n);
		build(1,1,n);
		ans=0;
		for(i=1;i<=n;i++){
			v1[i].clear();
			v2[i].clear();
		}
		for(i=2;i<=n;i++){
			scanf("%d",&b[i]);
			v1[b[i]].push_back(i);
		}
		for(i=2;i<=n;i++){
			scanf("%d",&c[i]);
			v2[c[i]].push_back(i);
		}
		tot=0;
		dfs2(1,0);
		dfs1(1,0,0);
		printf("%d\n",ans);
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值