BZOJ传送门
洛谷传送门
解析:
写这个题面的真的厉害。。。
简单叙述一下题意:
给出一个序列 a a a,一个下标的排列 p p p是合法的,当且仅当对于每个 j j j,当前数为 a p [ j ] a_{p[j]} ap[j],且 a p [ j ] a_{p[j]} ap[j]的值没有在下标数组 p p p的第 j j j个位置及以后出现过。
可以这么理解, a a a是原数组, p p p下标数组。
换句话说, a j a_j aj在下标数组中要比 j j j先出现。
其他题解这里说的很模糊,出现?什么出现?到底是出现在原数组还是下标数组?
然后给出权值数组 w w w,定义一个合法下标数组有贡献,且贡献为 ∑ i = 1 n i w p [ i ] \sum\limits_{i=1}^niw_{p[i]} i=1∑niwp[i],求最大贡献
换句话说,原数组和权值数组的一个值是捆绑在了一起的。
现在考虑判断合法性。
刚才转化题意得到 a j a_j aj在下标数组中要比 j j j先出现。
则我们连边 a j → j a_j\rightarrow j aj→j来表示这个限制。
显然最后建出来的图如果有环就会产生矛盾。
发现这是一个有 n + 1 n+1 n+1个点 [ 0 , n ] [0,n] [0,n],和 n n n条边(显然不可能有重边)的图,所以只需要一次 d f s 0 dfs0 dfs0就能够判断是否有环了。
不然的话,图就是一棵树。
现在考虑怎么求最大的贡献
考虑从前面的,也就是系数小的开始贪心。
考虑当前权值最小点 i i i。
如果 i i i没有父亲我们就肯定直接选择 i i i了。
如果 i i i有父亲,那么选择了父亲之后肯定是立即选择它,所以我们可以将它和父亲合并,记录 s i z siz siz和权值。
最后显然就是在一堆序列里面贪心做拼接。
考虑长度为 m 1 m_1 m1的序列 1 1 1和长度为 m 2 m_2 m2的序列 b b b,我们尝试在 a b ab ab和 b a ba ba两种拼接中找出最大值。
W a b = ∑ i = 1 m 1 i w a i + ∑ i = 1 m 2 ( i + m 1 ) w b i W b a = ∑ i = 1 m 2 i w b i + ∑ i = 1 m 1 ( i + m 2 ) w a i \begin{aligned} W_{ab}&=&&\sum_{i=1}^{m_1}iw_{a_i}+\sum_{i=1}^{m_2}(i+m_1)w_{b_i}\\ W_{ba}&=&&\sum_{i=1}^{m_2}iw_{b_i}+\sum_{i=1}^{m_1}(i+m_2)w_{a_i}\\ \end{aligned} WabWba==i=1∑m1iwai+i=1∑m2(i+m1)wbii=1∑m2iwbi+i=1∑m1(i+m2)wai
做差得到:
W a b − W b a = m 1 W b − m 2 W a W a b > W b a    ⟺    W b m 2 > W a m 1 W_{ab}-W_{ba}=m_1W_b-m_2W_a\\W_{ab} > W_{ba}\iff \frac{W_b}{m_2} > \frac{W_a}{m_1} Wab−Wba=m1Wb−m2WaWab>Wba⟺m2Wb>m1Wa
换句话说,我们在前面要尽可能选择平均值较小的序列。
堆或者平衡树维护一下所有序列中最小平均值就行了。
比较还是化除法为乘法,避免精度误差。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
struct data{
int u,siz;
ll val;
friend bool operator<(cs data &a,cs data &b){return a.val*b.siz>b.val*a.siz;}
};
priority_queue<data> s;
cs int N=5e5+5;
int n,cnt;
int fa[N];
vector<int> son[N];
int siz[N],bel[N];
ll w[N];
bool vis[N];
void dfs(int u){
vis[u]=true;
++cnt;
for(int re e=0;e<son[u].size();++e)
if(vis[son[u][e]])puts("-1"),exit(0);
else dfs(son[u][e]);
}
inline int getfa(int u){
while(u^bel[u])u=bel[u]=bel[bel[u]];
return u;
}
ll ans=0;
signed main(){
n=getint();
for(int re i=1;i<=n;++i)son[fa[i]=getint()].push_back(i);
dfs(0);
if(cnt<=n)return puts("-1"),0;
siz[0]=1;
for(int re i=1;i<=n;++i){
w[i]=getint();
siz[i]=1;
bel[i]=i;
s.push((data){i,1,w[i]});
}
while(!s.empty()){
data tmp=s.top();s.pop();
if(tmp.siz^siz[getfa(tmp.u)])continue;
int u=getfa(tmp.u);
int p=bel[u]=getfa(fa[u]);
ans+=siz[p]*w[u];siz[p]+=siz[u],w[p]+=w[u];
if(p)s.push((data){p,siz[p],w[p]});
}
cout<<ans<<"\n";
return 0;
}