如果
\(j<=k,a_{p[j]}!=p[k]\)可以理解为如果
\(a_{p[j]}=p[k]\),那么
\(k\)一定要放在
\(j\)前面,也就是
\(a_j\)在
\(j\)前面。
于是连边
\((a[j],j)\),表示
\(a[j]\)在
\(j\)前面,如果有环就是无解,如果没有环那么一定是一棵以
\(0\)为根的树
根据题意,我们肯定是要尽量把
\(w\)小的安排在前面。
于是考虑这样一个贪心:
对于当前最小
\(w_i\) 如果
\(fa[i]=0\),那么肯定直接选
\(i\) 如果
\(fa[i]!=0\),那么当选完
\(fa[i]\)后肯定马上选
\(i\) 于是就可以把
\(fa[i]\)和
\(i\)合并了。
合并了以后显然不能按权值和排序,而要按权值的平均值。
维护当前最小可以用堆,
但合并后要修改权值,可以用平板电视的带修改堆,或者标记一下有没有被合并。
统计答案类似秦九韶定理。
#include #include using namespace std;inline int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * w;}const int MAXN = 500010;struct Edge{ int next, to;}e[MAXN];int head[MAXN], num;inline void Add(int from, int to){ e[++num].to = to; e[num].next = head[from]; head[from] = num;}struct node{ int size, id; long long val; int operator > (const node A) const{ return val * A.size > A.val * size; }}now;int n, p;long long ans;int vis[MAXN], f[MAXN], size[MAXN], fa[MAXN];long long w[MAXN];priority_queue , greater > q;int dfs(int u){ vis[u] = 1; for(int i = head[u]; i; i = e[i].next) if(vis[e[i].to]) return 1; else if(dfs(e[i].to)) return 1; return 0;}int find(int x){ return f[x] == x ? x : f[x] = find(f[x]);}int main(){ n = read(); size[0] = 1; for(int i = 1; i <= n; ++i) Add(fa[i] = read(), f[i] = i); if(!head[0] || dfs(0)){ printf("-1"); return 0; } for(int i = 1; i <= n; ++i){ w[i] = read(); q.push( (node){ size[i] = 1, i, w[i] } ); } while(q.size()){ now = q.top(); q.pop(); if(size[now.id] ^ now.size) continue; f[now.id] = p = find(fa[now.id]); ans += w[now.id] * size[p]; size[p] += size[now.id]; w[p] += w[now.id]; if(p) q.push((node){ size[p], p, w[p] }); } printf("%lld\n", ans); return 0;}