题意:https://ac.nowcoder.com/acm/contest/11258/F 给出两树S1, S2, 求一个点集,满足这个点集上的点都在S1上的一条链上(且联通),并且在S2中,这个点集任意两点没有祖先关系。
首先, 我们可以先预处理S2, 求出S2中每个点的dfs序,并记录这个点及其的后代的dfs序的范围,这样一来,每个点的dfs 序就都是一个连续的区间,且该点的后代的dfs序区间包含在它的祖先的区间里,这样操作的好处在于如果两个节点没有祖先关系,那他们的dfs序的区间就不会互相包含。
接下来,我们可以 dfs S1,对每条链进行处理,注意到在S1中, 这个集合的点是相连的, 那么也就是说这些点在一条链上的位置是相邻的,然后要找出的是这个区间长度的最大值,由于我说不清楚,我们可以想到用滑动窗口来处理。只要能够判断出新进的点能够满足与窗口里的点没有互相包含的区间,那么这个区间的右端就可以延申下去,否则,我们就得将窗口的左端慢慢移到右端,并进行判断,直到满足条件,即为一个合理的区间。
那么问题来了,怎么判断呢? 我们可以想到, 当一个区间出现过时,我们可以将这个区间里所有数都加1,并维护起来。当dfs到另一个点时, 我们也对这个新进的点的区间里所有数加1,然后查询这个区间和,如果这个区间和刚好等于区间长度,那么这个区间就是没被包含过也没包含过其他区间,否则,窗口的左端就得左移,并对移出来的点的区间减1,直到刚刚处理的新进来的点的区间和等于其区间长度,然后维护区间最大值。
这里要注意, 在每走一步的回溯时,我们要把窗口和维护区间的数据结构还原到这个新进的点没处理前的样子,这一步结合代码应该很好理解。
对于区间修改, 区间查询我们可以用线段树,树状数组等数据结构实现,下面的代码给出的是一个树状数组的方法。
理论复杂度 nlogn
#include <bits/stdc++.h>
#pragma GCC optimize(2)
#define pb push_back
#define endl '\n'
using namespace std;
using ll = long long;
const int maxn = 3e5 + 10;
vector <int> tree1[maxn], tree2[maxn];
int L[maxn], R[maxn];//第二颗树的dfs序的范围
//树状数组维护区间和
int tree[maxn];
int sum[maxn];
//
int kk;
int ans = 0;
void dfs(int num, int last) //求dfs序
{
L[num] = ++kk;
for (int i : tree2[num])
{
if (i == last) continue;
dfs(i, num);
}
R[num] = kk;
}
int lef = 0, rig = -1, n;
int p[maxn];
//树状数组处理过程
void update(int id, int val)
{
int y = id;
while (id < n + 2)
{
tree[id] += val;
sum[id] += val * y;
id += (id & (-id));
}
}
int query(int num)
{
int a = 0, y = num;
while (num > 0)
{
a += (y + 1) * tree[num] - sum[num];
num -= (num & (-num));
}
return a;
}
//对第一棵树的每一条链进行滑动窗口处理
void dfs1(int num, int last)
{
int l = lef, r = rig;
p[++rig] = num;
update(L[num], 1);
update(R[num] + 1, -1);
while (query(R[num]) - query(L[num] - 1) > R[num] - L[num] + 1)
{
int t = p[lef];
update(L[t], -1);
update(R[t] + 1, 1);
lef++;
}
ans = max(ans, rig - lef + 1);
for (int i : tree1[num])
{
if (i == last) continue;
dfs1(i, num);
}
//还原到该点没有处理前的样子
update(L[num], -1);
update(R[num] + 1, 1);
rig--;
while (lef > l)
{
lef--;
update(L[p[lef]], 1);
update(R[p[lef]] + 1, -1);
}
}
void solve()
{
int i, j, u, v;
cin >> n;
lef = 0, rig = -1;
kk = 0; ans = 0;
for (i = 1; i <= n; i++) tree1[i].clear(), tree2[i].clear();
memset(tree, 0, sizeof(int) * (n + 10));
memset(sum, 0, sizeof(int) * (n + 10));
memset(L, 0, sizeof(int) * (n + 10));
memset(R, 0, sizeof(int) * (n + 10));
for (i = 1; i < n; i++)
{
cin >> u >> v;
tree1[u].pb(v);
tree1[v].pb(u);
}
for (i = 1; i < n; i++)
{
cin >> u >> v;
tree2[u].pb(v);
tree2[v].pb(u);
}
dfs(1, -1);
dfs1(1, -1);
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
/*
1
5
2 1
2 5
5 3
2 4
2 1
1 5
1 3
3 4
*/