2020 CCPC-Wannafly Winter Camp Day2 (Div.1&2) E 阔力梯的树(DSU on tree)

本文详细解析了2020年CCPC-WannaflyWinterCampDay2(Div.1&2)E题——阔力梯的树。介绍了使用DSUontree算法结合树链剖分解决子树问题的方法,通过代码实现展示了如何利用set进行子树贡献统计,适用于静态子树问题的求解。

2020 CCPC-Wannafly Winter Camp Day2 (Div.1&2) E 阔力梯的树

题目

在这里插入图片描述

思路

暴力肯定不行,因为是静态+子树问题,用DSU on tree,前置是一丢丢的树链剖分,然后两个都学了,大致思路就是用dsu on tree + set去统计子树的贡献。

代码

#include <iostream>
#include <cstdio>
#include <set>
#include <list>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <string>
#include <sstream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <fstream>
#include <iomanip>
//#include <unordered_map>
using namespace std;
#define dbg(x) cerr << #x " = " << x << endl;
typedef pair<int, int> P;
typedef long long ll;
#define FIN freopen("in.txt", "r", stdin);
const int MAXN = 1e5 + 5;
vector<int> v[MAXN];
int son[MAXN], sz[MAXN], vis[MAXN];
void dfs(int u) //找重儿子
{
    sz[u] = 1;
    //dbg(u);
    for (int i = 0; i < v[u].size(); i++)
    {
        int vv = v[u][i];
        dfs(vv);
        sz[u] += sz[vv];
        if (sz[vv] > sz[son[u]])
            son[u] = vv;
    }
    return;
}
ll ans[MAXN];
set<int> st;
ll cur = 0;
ll sum = 0;

void add(int num)
{
    set<int>::iterator it = st.lower_bound(num), start = st.begin(), end = st.end();
    end--;
    auto pre = it, next = it;
    if (it != start)
        pre--;
    if (it != end)
        next++;
    if (pre != it && next != it) //当前插入的数在中间
    {
        sum -= 1LL * (*next - *pre) * (*next - *pre);
        sum += 1LL * (*next - *it) * (*next - *it);
        sum += 1LL * (*pre - *it) * (*pre - *it);
    }
    else if (pre != it) //在最后
    {
        sum += 1LL * (*pre - *it) * (*pre - *it);
    }
    else if (next != it) //在开头
    {
        sum += 1LL * (*next - *it) * (*next - *it);
    }
    else //只有一个数
    {
        sum = 0;
    }
}

void del(int num)
{
    set<int>::iterator it = st.lower_bound(num), start = st.begin(), end = st.end();
    end--;
    auto pre = it, next = it;
    if (it != start)
        pre--;
    if (it != end)
        next++;
    if (pre != it && next != it) //当前删除的数在中间
    {
        sum += 1LL * (*next - *pre) * (*next - *pre);
        sum -= 1LL * (*next - *it) * (*next - *it);
        sum -= 1LL * (*pre - *it) * (*pre - *it);
    }
    else if (pre != it) //在最后
    {
        sum -= 1LL * (*pre - *it) * (*pre - *it);
    }
    else if (next != it) //在开头
    {
        sum -= 1LL * (*next - *it) * (*next - *it);
    }
    else //只有一个数
    {
        sum = 0;
    }
}

void update(int u, int val)//更新
{
    if (val == 1)//插入数据 更新
    {
        st.insert(u);
        add(u);
    }
    else//删除数据 更新
    {
        del(u);
        st.erase(u);
    }
    for (auto vv : v[u])//对于子树 一样更新
    {
        if (vis[vv])//重儿子不用管 暴力计算轻儿子
            continue;
        update(vv, val);
    }
}

void dfs2(int u, int keep)//计算部分 keep决定是否保留
{
    for (auto vv : v[u])//先计算轻儿子
    {
        if (vv == son[u])
            continue;
        dfs2(vv, 0);
    }
    if (son[u])//在计算重儿子
    {
        dfs2(son[u], 1);
        vis[son[u]] = 1;
    }
    update(u, 1);
    ans[u] = sum;
    if (son[u])
        vis[son[u]] = 0;
    if (!keep)
        update(u, -1);
}

int main()
{
    //FIN;
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    for (int i = 2; i <= n; i++)
    {
        int num;
        cin >> num;
        v[num].push_back(i);
    }
    dfs(1);
    dfs2(1, 1);
    for (int i = 1; i <= n; i++)
    {
        cout << ans[i] << endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值