2024内部排位赛补题2
题意:
给定有
n
n
n个节点的带点权的两棵树,同时给出
k
k
k个点,要求删除一个点,使得
A
A
A树剩余
k
−
1
k - 1
k−1个点的
L
C
A
LCA
LCA权值比
B
B
B剩余
k
−
1
k - 1
k−1个点的
L
C
A
LCA
LCA权值要大,求有多少种方案
t r i c k : L C A trick:LCA trick:LCA满足前缀和差分的性质,可以通过差分求取一段区间的 L C A LCA LCA
题解:
首先预处理出
k
k
k个点的
d
f
n
dfn
dfn序,然后求一遍前缀
L
C
A
LCA
LCA和后缀
L
C
A
LCA
LCA,当我们要删除的点是
d
f
n
dfn
dfn序上第
i
i
i个点时,剩余
k
−
1
k - 1
k−1个点的
L
C
A
LCA
LCA即
l
c
a
(
p
r
e
[
i
−
1
]
,
s
u
f
[
i
+
1
)
]
lca(pre[i - 1], suf[i + 1)]
lca(pre[i−1],suf[i+1)],因此通过这样的转移方式可以从左向右遍历一遍
d
f
n
dfn
dfn序,处理出
A
,
B
A,B
A,B两树的每个点删除后的权值答案,最后比较累加贡献即可。
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define debug(p) for(auto i:p)cerr<<i<<" ";cerr<<endl;
#define debugs(p) for(auto i:p)cerr<<i.first<<" "<<i.second<<endl;
typedef pair<int,int> pll;
string yes="YES";
string no="NO";
constexpr int N = 1e5 + 7;
bool ned[N];
struct LCA
{
vector<int>edge[N];
int w[N], sub[N], pre[N], suf[N];
int idx;
int dfn[N], pos[N];
int depth[N], fa[N][20];//fa记录从编号为i的点开始向上跳2^k步所达到的点的编号
void dfs(int u, int f) {
if(ned[u])
{
dfn[++idx] = u;
pos[u] = idx;
}
for (auto v : edge[u]) {
if (v == f)
continue;
depth[v] = depth[u] + 1;//预处理每个点的深度
fa[v][0] = u;
for (int i = 1; i < 20; i++)
fa[v][i] = fa[fa[v][i - 1]][i - 1];//预处理所有点向上跳倍增所达到的编号
dfs(v, u);
}
return;
}
int lca(int a, int b) {
if(a == 0 || b == 0)return a | b;
if (depth[a] < depth[b])
swap(a, b);
for (int i = 19; i >= 0; i--) {//让a和b达到同一水平线
if (depth[fa[a][i]] >= depth[b])
a = fa[a][i];
}
if (a == b)//如果达到同一水平线后,两者重合,返回该点编号
return a;
for (int i = 19; i >= 0; i--) {//反之,则两者继续向上跳倍增,直到他们两个的父节点为同一点
if (fa[a][i] != fa[b][i]) {
a = fa[a][i];
b = fa[b][i];
}
}
return fa[a][0];//返回两者达到的点
}
}a, b;
void solve()
{
int n, k;
cin >> n >> k;
vector<int>s(k + 1);
for (int i = 1; i <= k; i++)
{
cin >> s[i];
ned[s[i]] = 1;
}
for (int i = 1; i <= n; i++) cin >> a.w[i];
for (int i = 2; i <= n; i++)
{
int u;
cin >> u;
a.edge[u].push_back(i);
}
for (int i = 1; i <= n; i++) cin >> b.w[i];
for (int i = 2; i <= n; i++)
{
int u;
cin >> u;
b.edge[u].push_back(i);
}
a.depth[1] = 1;
b.depth[1] = 1;
a.dfs(1, -1);
b.dfs(1, -1);
for (int i = 1; i <= k; i++)
{
a.pre[i] = a.lca(a.pre[i - 1], a.dfn[i]);
b.pre[i] = b.lca(b.pre[i - 1], b.dfn[i]);
}
for (int i = k; i >= 1; i--)
{
a.suf[i] = a.lca(a.suf[i + 1], a.dfn[i]);
b.suf[i] = b.lca(b.suf[i + 1], b.dfn[i]);
}
for (int i = 1; i <= k; i++)
{
a.sub[a.dfn[i]] = a.w[a.lca(a.pre[i - 1], a.suf[i + 1])];
b.sub[b.dfn[i]] = b.w[b.lca(b.pre[i - 1], b.suf[i + 1])];
}
int ans = 0;
for (int i = 1; i <= k; i++)
{
if(a.sub[s[i]] > b.sub[s[i]]) ans++;
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while(T--)
{
solve();
}
}