题意:
题解:
感觉很难啊,听说考场乱搞能拿70……
容易转化题意为在森林上移除根节点,每次获得的分数是权值乘上操作次数。
然后就想了各种 随手卡的 sb贪心。
正解挺好的,思路上是每次找到权值最小的点(因为这样就保证当父亲被选了后立刻就选到他),将他和父亲缩成一个点,并且计算这个点在这个联通块的贡献,直到所有点缩成一个点。
关键就是怎么确定新点的权值。
答案就是平均值。
例如:
现在有两个联通块,权值分别是
X,Y
X
,
Y
,大小是
n,m
n
,
m
。忽略掉共同对答案的贡献,那么就只剩下这两个联通块相互的影响,分别是
nY,mX
n
Y
,
m
X
,移项可得
Ym,Xn
Y
m
,
X
n
。
code:
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
typedef pair<long double,LL> pi;
LL n,a[500005],fa[500005],f[500005],w[500005],size[500005];
LL findfa(LL x) {return fa[x]==x?x:fa[x]=findfa(fa[x]);}
struct Queue{
priority_queue<pi> a,b;
void push(pi x) {a.push(x);}
void pop()
{
while(!b.empty()&&a.top()==b.top()) a.pop(),b.pop();
a.pop();
}
void erase(pi x) {b.push(x);}
pi top()
{
while(!b.empty()&&a.top()==b.top()) a.pop(),b.pop();
return a.top();
}
}q;
int main()
{
scanf("%lld",&n);
for(LL i=0;i<=n;i++) fa[i]=i;
for(LL i=1;i<=n;i++)
{
scanf("%lld",&f[i]);
if(fa[findfa(i)]==fa[findfa(f[i])]) return puts("-1"),0;
fa[findfa(i)]=findfa(f[i]);
}
LL ans=0;
for(LL i=1;i<=n;i++) scanf("%lld",&w[i]),ans+=w[i];
for(LL i=1;i<=n;i++) fa[i]=i,size[i]=1,q.push(make_pair(-(long double)w[i],i));
for(LL i=1;i<=n;i++)
{
LL x=q.top().second,tx=findfa(f[x]);q.pop();
if(tx) q.erase(make_pair(-(long double)w[tx]/size[tx],tx));
ans+=size[tx]*w[x];
w[tx]+=w[x];size[tx]+=size[x];fa[x]=tx;
if(tx) q.push(make_pair(-(long double)w[tx]/size[tx],tx));
}
printf("%lld",ans);
}