题解:P17851 「RiOI-(-2)」houran 和芒果 / [ROI 2026 Day1] 泰莫利亚的调查 加强版
很牛的题。
我们要求的就是
选择
m 个叶子,每个点带来的贡献是\max_{i\in S_u} w_i ,最大化贡献和。
我们发现这个
在这棵新树上原本的边权覆盖转化成了点权覆盖。因此问题变成:
求
m 条链覆盖能够得到的点权和最大值。
这个就是经典问题了,有一个显然的贪心就是每次选择贡献最大的点,发现这个过程等价于按边权为权重进行长链剖分,然后取前
但是进一步分析可以发现不用真的把虚点建出来,考虑向上合并时长链信息如何更新。令
此题要求线性,因此不能对每个 nth_element 即可做到线性。因此总时间复杂度
upd:这个做法如果写的不好看是会被卡常的,注意不要用 vector 存每个点的儿子,改成按照
:::success[代码]
#include <bits/stdc++.h>
#define psb push_back
#define lc (u<<1)
#define rc (u<<1|1)
using namespace std;
typedef long long ll;
template<class T>void Min(T &x,T y) { if (y<x) x=y; }
template<class T>void Max(T &x,T y) { if (y>x) x=y; }
const ll INF=1e18;
const int N=200010;
int n,m; int fa[N], w[N];
int cnt[N], id[N], ord[N]; ll buc[N]; int tot;
int lson[N], rson[N]; ll d[N];
void work()
{
for (int i=0;i<(1<<17);i++) cnt[i]=0;
for (int i=2;i<=n;i++) cnt[w[i]]++;
for (int i=1;i<(1<<17);i++) cnt[i]+=cnt[i-1];
for (int i=n;i>=2;i--) id[cnt[w[i]]--]=i;
memset(cnt,0,sizeof(int)*(n+1));
for (int i=1;i<n;i++) cnt[fa[id[i]]]++;
for (int i=2;i<=n;i++) cnt[i]+=cnt[i-1];
for (int i=n-1;i;i--) ord[cnt[fa[id[i]]]--]=id[i];
for (int i=1;i<=n;i++) lson[i]=n+1, rson[i]=0;
for (int i=1;i<n;i++) Min(lson[fa[ord[i]]],i), Max(rson[fa[ord[i]]],i);
tot=0;
for (int u=n;u;u--)
{
d[u]=0;
for (int i=rson[u];i>=lson[u];i--)
{
int v=ord[i];
ll delta=w[v]-(i>lson[u] ? w[ord[i-1]] : 0);
if (d[v]>d[u]) swap(d[u], d[v]);
d[u]+=delta; if (d[v]) buc[++tot]=d[v];
}
}
buc[++tot]=d[1];
m=min(m,tot); nth_element(buc+1,buc+m,buc+tot+1,greater<ll>());
ll ans=0; for (int i=1;i<=m;i++) ans+=buc[i];
cout << ans << "\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i=2;i<=n;i++) cin >> fa[i];
for (int i=2;i<=n;i++) cin >> w[i];
int T; cin >> T;
while (T--)
{
int bse[4]; cin >> bse[0] >> bse[1] >> bse[2] >> bse[3] >> m;
for (int i=2;i<=n;i++) w[i]=w[i]^bse[i&3];
work();
for (int i=2;i<=n;i++) w[i]=w[i]^bse[i&3];
}
return 0;
}
:::