懒得粘题面…
我们发现限制是一个外向树结构,选一个点必须先选父亲,第 i 次选择一个点,价值是 i*点权。求最大价值。
DP 好像做不了,只能贪心了。考虑点权最小的点 x,一定是紧接着父亲被选。因此我们可以把 x 和父亲合并,看做一个点,同时计算他们之间的贡献。
那么缩点后的若干个联通块怎么安排顺序?类似国王游戏,记 S 为权值和,size 为合并前点数,那么 1 在 s 前面当且仅当
S
1
s
i
z
e
2
<
S
2
s
i
z
e
1
S_1size_2<S_2size_1
S1size2<S2size1,就是
S
1
/
s
i
z
e
i
<
S
2
/
s
i
z
e
2
S_1/size_i<S_2/size_2
S1/sizei<S2/size2。把这个作为合并后的权值即可。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fir first
#define sec second
#define ld long double
using namespace std;
const int N=500010;
typedef pair <double,int> P;
struct edge {
int to,next;
}ed[N<<2];
struct node {
ll w;int size,x;
node(ll _w=0,int _size=0,int _x=0) {w=_w,size=_size,x=_x;}
};
bool operator < (node a,node b) {return 1ll*a.w*b.size>1ll*b.w*a.size;}
priority_queue <node> q;
int ff[N],size[N],sz,head[N],fa[N],tim,vis[N];
ll w[N];
void add_edge(int from,int to)
{
ed[++sz].to=to;
ed[sz].next=head[from];
head[from]=sz;
}
int read()
{
int x=0;char c=getchar(),flag='+';
while(!isdigit(c)) flag=c,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return flag=='-'?-x:x;
}
void dfs(int u)
{
vis[u]=tim;
for(int i=head[u];i;i=ed[i].next)
{
int v=ed[i].to;
if(vis[v]==tim) {puts("-1");exit(0);}
if(vis[v]) continue;
dfs(v);
}
}
int find(int x)
{
int y=x;
while(y^fa[y]) y=fa[y];
while(x^y)
{
int tmp=fa[x];
fa[x]=y;
x=tmp;
}
return y;
}
int main()
{
ll ans=0;
int n=read();
for(int i=1;i<=n;i++) ff[i]=read(),add_edge(i,ff[i]);
for(int i=1;i<=n;i++) w[i]=read(),fa[i]=i,size[i]=1;
for(int i=1;i<=n;i++) tim++,dfs(i),q.push(node(w[i],1,i));
size[0]=1;
while(q.size())
{
node u=q.top();
q.pop();
int x=find(u.x);
if(u.size^size[x]) continue;
int y=find(ff[x]);
ans+=1ll*size[y]*w[x];
w[y]+=w[x];
size[y]+=size[x];
fa[x]=y;
if(y) q.push(node(w[y],size[y],y));
}
cout<<ans;
return 0;
}
/*by DT_Kang*/