题意
给你n个点的树,你要构造出一些点集,使得每个点集里面的任何点对都不存在祖先关系,使得每个点集的最大值的和最小
n
≤
2
∗
1
0
5
n\leq 2*10^5
n≤2∗105
分析
发现对于点集的数量其实和深度有关系
仔细分析,不同子树内的点集其实可以最大的和最大的消,次大的和次大的消,如果是祖先的话,就只能新开一个集合
这样的话用长链剖分就好了 拿轻链去跟重链消,每条重链只会贡献这条重链长度的点集,每条重链只有在轻边的时候进行统计,这样总共会被统计 n n n次,开个堆就行了
代码
#include <bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace __gnu_pbds;
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> splay;
const int N = 200010;
const int mod = 998244353;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
struct node{int x,y,nex;}edge[N<<1]; int len,fir[N];
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].nex = fir[x]; fir[x] = len;}
int n,m,a[N],son[N],dep[N];
void dfs(int x)
{
dep[x] = 1;
for(int k=fir[x];k!=-1;k=edge[k].nex){int y = edge[k].y; dfs(y); dep[x] = max(dep[y] + 1,dep[x]);}
for(int k=fir[x];k!=-1;k=edge[k].nex){int y = edge[k].y; if(dep[y] + 1 == dep[x]) son[x] = y;}
}
priority_queue<int> q[N]; int id[N]; int sz = 0;
int tmp[N],tot;
void dfs2(int x)
{
if(son[x])
{
dfs2(son[x]);
id[x] = id[son[x]];
}else id[x] = ++sz;
for(int k=fir[x];k!=-1;k=edge[k].nex)
{
int y = edge[k].y;
if(y==son[x]) continue; dfs2(y);
tot = 0; while(q[id[y]].size())
{
tmp[++tot] = max(q[id[y]].top() , q[id[x]].top());
q[id[y]].pop(); q[id[x]].pop();
}while(tot){q[id[x]].push(tmp[tot]); tot--;}
}
// printf("%d %d\n",x,q[id[x]].size());
q[id[x]].push(a[x]);
}
int main()
{
len = 0; memset(fir,-1,sizeof(fir));
n = read(); for(int i=1;i<=n;i++) a[i] = read();
for(int i=2;i<=n;i++) ins(read(),i);
dfs(1);
sz = 0; dfs2(1);
// for(int i=1;i<=n;i++) printf("%d ",id[i]); printf("\n");
ll s = 0; while(!q[id[1]].empty()) s+=q[id[1]].top(),q[id[1]].pop();
return printf("%lld\n",s),0;
}