2024内部排位赛补题(LCA)

2024内部排位赛补题2

在这里插入图片描述
题意:
给定有 n n n个节点的带点权的两棵树,同时给出 k k k个点,要求删除一个点,使得 A A A树剩余 k − 1 k - 1 k1个点的 L C A LCA LCA权值比 B B B剩余 k − 1 k - 1 k1个点的 L C A LCA LCA权值要大,求有多少种方案

t r i c k : L C A trick:LCA trickLCA满足前缀和差分的性质,可以通过差分求取一段区间的 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 k1个点的 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[i1],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();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值