解题思路:
首先要看出题目要求的就是一棵树(有环无解),求一个顺序,父亲必须再儿子前选,选第i个点的价值是i*w[i],求最大价值和。
然后这确实是雅礼集训的原题。
先来考虑一个简单的情况:若v是u的儿子且是所有可选点中最小的。那么选了u之后下一个一定会选v。那么我们可以把u与v合并。
这个结论对于一个连通块也是对的,所以块与块之间也可以比较大小,且比较的是平均值。因为考虑连通块
i
i
和谁先选,即:
wi∗si+wj∗(si+sj)>wj∗sj+wi∗(si+sj)⇒wisi<wjsj
w
i
∗
s
i
+
w
j
∗
(
s
i
+
s
j
)
>
w
j
∗
s
j
+
w
i
∗
(
s
i
+
s
j
)
⇒
w
i
s
i
<
w
j
s
j
所以我们可以每次找出权值最小的联通快,将其与他的父亲合并。用并查集+堆即可实现。
#include<bits/stdc++.h>
#define double long double
#define ll long long
#define mp make_pair
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=500005;
int n,fa[N],s[N],id[N];
ll w[N],ans;
struct Queue
{
priority_queue<pair<double,int> >a,b;
void push(pair<double,int> x){a.push(x);}
void erase(pair<double,int> x){b.push(x);}
void pop()
{
while(!b.empty()&&a.top()==b.top())a.pop(),b.pop();
a.pop();
}
pair<double,int> top()
{
while(!b.empty()&&a.top()==b.top())a.pop(),b.pop();
return a.top();
}
}q;
int find(int x){return id[x]==x?x:id[x]=find(id[x]);}
int main()
{
//freopen("perm.in","r",stdin);
//freopen("perm.out","w",stdout);
n=getint();int x,y;
for(int i=1;i<=n;i++)id[i]=i;
for(int i=1;i<=n;i++)
{
fa[i]=getint();x=find(i),y=find(fa[i]);
if(x==y){puts("-1");return 0;}
id[x]=y;
}
id[0]=0;
for(int i=1;i<=n;i++)
{
ans+=w[i]=getint(),s[i]=1,id[i]=i;
q.push(mp(-(double)w[i],i));
}
for(int i=1;i<=n;i++)
{
x=q.top().second,q.pop(),y=find(fa[x]);
if(y)q.erase(mp(-(double)w[y]/s[y],y));
ans+=w[x]*s[y];
w[y]+=w[x],s[y]+=s[x],id[x]=y;
if(y)q.push(mp(-(double)w[y]/s[y],y));
}
cout<<ans<<'\n';
return 0;
}