题目大意:
给定两颗根为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
*/