牛客多校2021 F.xay loves trees(树状数组+树上的滑动窗口)
Posted li_wen_zhuo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客多校2021 F.xay loves trees(树状数组+树上的滑动窗口)相关的知识,希望对你有一定的参考价值。
题目描述
You have two trees rooted at 1 that both have n nodes. You need to find the largest subset of {1,2,⋯,n} such that:
- On the first tree, the set is connected, and for any two points u, v in set, one of u or v is an ancestor of the other.
- On the second tree, for any two points u, v in set, none of u or v is an ancestor of the other.
Output the biggest size.
输入描述
The first line contains an integer t (1≤t≤10) — the number of testcases. The description of the testcases follows.
The first line of each testcase contains an integer n (1≤n≤3×10^5).
Each of the next n-1 lines contains two integers ui,vi (1≤ui ,vi≤n; ui != vi) — Represents an edge on the first tree.
Each of the next n-1 lines contains two integers ui,vi(1≤ui,vi≤n; ui != vi) — Represents an edge on the second tree.
It is guaranteed that the given graphs are trees.
It is guaranteed that the sum of n over all testcases doesn’t exceed 3×10^5.
输出描述
For each testcase print a single integer — the size of the maximum subset.
示例
输入
3
5
2 1
2 5
5 3
2 4
2 1
1 5
1 3
3 4
5
5 3
3 1
5 4
3 2
5 3
5 1
3 4
3 2
5
5 3
5 1
5 2
5 4
5 3
3 1
5 2
3 4
输出
3
1
2
题目大意
用n个点组成两颗树,我们要找出最大的子集,满足如下的条件:
1)该点集在树1上表现为一条链。
2)该点集在树2上,点集中的所有点没有任何祖先关系。
题目分析
我们首先看条件一,我们可以用深度优先遍历的方式找出树上所有的链
O(n)(即所有满足条件1的部分)。
找出这些链之后,我们要筛选出满足条件二的最大点集来。为了判断两点之间是否存在祖先关系,我们可以求出树2的dfs序,并记录每个点的范围区间。如果两个点之间有祖先关系,那么他们的范围区间一定是重合的;而如果两个点之间没有祖先关系,那么他们的范围区间一定没有重合。
我们可以把在树1的上找出的链(点集)放入滑动窗口中,每向队列中放入一个点,我们就可以用覆盖一遍该点的dfs区间(该操作可以用树状数组+差分完成)。如果该区间已被覆盖过,说明点集中存在祖先关系,我们就需要删除队列中的一些点。
这样遍历所有的链,并记录满足条件的最大点集即可。
代码如下
#include <iostream>
#include <cmath>
#include <cstdio>
#include <set>
#include <string>
#include <cstring>
#include <map>
#include <algorithm>
#include <stack>
#include <queue>
#include <bitset>
#define LL long long
#define ULL unsigned long long
#define PII pair<LL,LL>
#define PDD pair<double,double>
#define x first
#define y second
using namespace std;
const int N=1e6+5,INF=1e9+7;
int n,ans,cnt;
vector<int> g1[N],g2[N];
int L[N],R[N],q[N];
int tr1[N],tr2[N];
int lowbit(int x) //树状数组模板
{
return x&-x;
}
void add(int x,int c)
{
for(int i=x;i<=n;i+=lowbit(i))
tr1[i]+=c,tr2[i]+=c*x;
}
int sum(int x)
{
int res=0;
for(int i=x;i;i-=lowbit(i))
res+=tr1[i]*(x+1)-tr2[i];
return res;
}
void dfs(int u,int fa) //求出树2的dfs序
{
L[u]=++cnt;
for(int i=0;i<g2[u].size();i++)
if(g2[u][i]!=fa) dfs(g2[u][i],u);
R[u]=cnt;
}
void dfs2(int u,int fa,int l,int r) //dfs树1,找出所有满足条件的点集(枚举树1的所有链),并进行处理
{
int ls=l,rs=r; //记录当前的区间状态(后面需要进行回溯)
q[++r]=u; //将u放入滑动窗口
add(L[u],1),add(R[u]+1,-1); //更新覆盖(对u点的dfs序区间进行覆盖)
while(sum(R[u])-sum(L[u]-1)>R[u]-L[u]+1) //如果u点的dfs序区间内存在重复覆盖,则从队首删除一些点,直到没有覆盖为止
{
int v=q[l];
add(L[v],-1),add(R[v]+1,1); //更新删除点的覆盖
l++;
}
ans=max(ans,r-l+1); //这样滑动窗口内的点就是满足了全部条件的,更新答案最大值
for(int v:g1[u]) //继续dfs
{
if(v==fa) continue;
dfs2(v,u,l,r);
}
r--; //因为要的是链,因此一条链枚举完成后,要进行回溯
add(L[u],-1),add(R[u]+1,1); //回溯u点
while(l>ls) //回溯到原来的左端点
{
l--;
int v=q[l];
add(L[v],1),add(R[v]+1,-1);
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
ans=cnt=0; //初始化
for(int i=1;i<=n;i++)
{
tr1[i]=tr2[i]=L[i]=R[i]=0;
g1[i].clear(),g2[i].clear();
}
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
g1[u].push_back(v);
g1[v].push_back(u);
}
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
g2[u].push_back(v);
g2[v].push_back(u);
}
dfs(1,-1);
dfs2(1,-1,0,-1);
printf("%d\\n",ans); //输出答案
}
return 0;
}
以上是关于牛客多校2021 F.xay loves trees(树状数组+树上的滑动窗口)的主要内容,如果未能解决你的问题,请参考以下文章
2021牛客暑期多校训练营7 F.xay loves trees 主席树+dfs序
2021牛客暑期多校训练营7 F.xay loves trees(主席树+树上尺取)
2021牛客多校7 - xay loves trees(dfs序+主席树-标记永久化)