2021牛客暑期多校训练营7 F xay loves trees

F xay loves trees

题目大意:

给定两颗根为1的树,求一个最大的集合,集合中的元素要满足

1、这些元素在树1中是从上到下的一条链(可以断开)

2.、这些元素在树2中,任意两个元素,不能在从上到下的一条链中

求最大集合的大小

比赛时的错误想法大赏:

错误想法1:

在树1中找到叶子节点,然后按深度排序,先找最长的,把这些元素在树2中标记出来,然后在第二棵树中利用树形dp求 m a x max max

先将树2中的元素标记为

f x = 1 f_x=1 fx=1

然后直接跑深搜

转移方程:

f x = m a x ( f x , ∑ f y ) f_x=max(f_x,\sum{f_y}) fx=max(fx,fy)

y y y是子节点

时间复杂度大于 O ( n 2 ) O(n^2) O(n2),所以TLE了

错误想法2:

树1的处理方式完全与之前相同,对于第二棵树,先标记,丛叶子节点向上找起,如果出现被标记的节点,就记录父节点,并不在往上找

问题在于,对于某些深度较前的叶子节点,会导致错误

正解:

利用dfs序和主席树进行求解

对于树2,可以利用出点和入点的思想进行求解,子树中的出点一定小于等于该节点的出点,子树中的入点一定大于等于其入点。

对于树1,可以在树上用栈维护一条链,入点的时候压栈,出来的时候弹栈,并在每一个点上建主席树,设一条链上的左右端点,然后进行二分。并记录二分结束时的左端,对于右端也是有限制的

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
#define inf 0x7fffffff
#define ll long long
//#define int long long
//#define double long double
#define re register int
#define void inline void
#define eps 1e-8
//#define mod 1e9+7
#define ls(p) p<<1
#define rs(p) p<<1|1
#define pi acos(-1.0)
#define pb push_back
#define P pair < int , int >
#define mk make_pair
using namespace std;
const int mod=1e9+7;
const int M=1e8+5;
const int N=6e5+5;//?????????? 4e8
vector < int >  g1[N],g2[N];
int in[N],out[N],num;
int instack[N],top,ans,pre[N],rt[N];
int n;
struct node
{
	int l,r,sum,add;
}e[N*40];
int tot;
void insert(int &p,int pre,int ql,int qr,int l,int r)
{
	e[++tot]=e[pre];
	p=tot;
	e[tot].sum+=(min(qr,r)-max(ql,l)+1);
	if(ql<=l&&r<=qr)
	{
		e[p].add++;
		return;
	}
	int mid=(l+r)>>1;
	if(ql<=mid)  insert(e[p].l,e[pre].l,ql,qr,l,mid);
	if(mid<qr)  insert(e[p].r,e[pre].r,ql,qr,mid+1,r);
}
int ask(int p,int pre,int ql,int qr,int l,int r)
{
	if(ql<=l&&r<=qr)  return e[p].sum-e[pre].sum;
	int mid=(l+r)>>1;
	int t=(min(qr,r)-max(ql,l)+1)*(e[p].add-e[pre].add);
	if(ql<=mid)  t+=ask(e[p].l,e[pre].l,ql,qr,l,mid);
	if(mid<qr)  t+=ask(e[p].r,e[pre].r,ql,qr,mid+1,r);
	return t; 
}
void dfs1(int x,int fa)
{
	in[x]=++num;
	int sz=g2[x].size();
	for(re i=0;i<sz;i++)
	{
		int y=g2[x][i];
		if(y==fa)  continue;
		dfs1(y,x);
	}
	out[x]=num;
}
void dfs2(int x,int fa)
{
	instack[++top]=x;
	rt[x]=rt[fa];
	int l=pre[fa],r=top;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(ask(rt[x],rt[instack[mid]],in[x],out[x],1,n))  l=mid+1;
		else  r=mid-1;
	}
	pre[x]=l;
	ans=max(ans,top-l);
	insert(rt[x],rt[fa],in[x],out[x],1,n);
	int sz=g1[x].size();
	for(re i=0;i<sz;i++)
	{
		int y=g1[x][i];
		if(y==fa)  continue;
		dfs2(y,x);
	}
	top--;
}
void init()
{
	for(re i=0;i<=tot+1;i++)  e[i].l=e[i].r=e[i].add=e[i].sum=0;
	top=num=tot=0;
	ans=1;
	for(re i=0;i<=2*n+2;i++)
	{
		g1[i].clear();g2[i].clear();
		in[i]=out[i]=pre[i]=instack[i]=rt[i]=0;
	} 
}
void solve()
{
	init();
	cin>>n;
	for(re i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		g1[x].pb(y),g1[y].pb(x);
	}
	for(re i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		g2[x].pb(y);g2[y].pb(x);
	}
	dfs1(1,1);dfs2(1,1);
	printf("%d\n",ans);
}
signed main()
{
    int T=1;
    cin>>T;
    for(int index=1;index<=T;index++)
    {
//    	printf("Case %d:\n",index);
        solve();
//        puts("");
    }
    return 0;
}
/*


1
6 5
0 0 0 122 499 8888




*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值