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;
}
}

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

被折叠的 条评论
为什么被折叠?



