题目背景
宫水三叶被要求出一场测试,于是三叶非常认真的出了一套题。
然而在临近测试时,一场意外使得这场测试所有的资料全部被清空了。其中包括题面,题解,标程和数据。
无奈之下,三叶找到了小H
,希望她能恢复这些被清空的数据。
题目描述
擅长电脑的小H
明白,这些数据是可恢复的,所以小H
决定帮助三叶。
由于某些原因,这些数据有依赖关系,复原一个数据需要复原它所依赖的数据。这些依赖关系形成一棵树。
对于一个数据,假设它有两个系数 a i , b i a_i,b_i ai,bi ,在复原这个数据时,因为还要保证没有被复原的数据的完整性,需要的代价为:
b i ⋅ ∑ j 还没被复原 a j b_i\cdot \sum_{j \text{还没被复原}} a_j bi⋅j还没被复原∑aj
小H
认为自己一定可以在规定时间内复原出数据,但是她想捉弄一下三叶。因此小H
想要让复原的时间尽可能长,也就是让上述代价尽量大。
现在你知道 n n n 个数据的系数和它们之间的依赖关系,请求出最大代价。
形式化题意
给定一棵 n n n 个点的树,对于所有 2 ≤ i ≤ n 2\le i \le n 2≤i≤n ,它的父亲节点为 f i f_i fi ,每一个点有两个系数 a i , b i a_i,b_i ai,bi 。
你需要求出一个长度为 n n n 的排列,满足对于 2 ≤ i ≤ n 2\le i \le n 2≤i≤n , f i f_i fi 都在 i i i 出现前出现。
这个排列的代价为:
∑ i = 1 n ( b p i ∑ j = i + 1 n a p j ) \sum_{i=1}^{n}(b_{p_i}\sum_{j=i+1}^{n}a_{p_j}) i=1∑n(bpij=i+1∑napj)
求最大代价。
第一行一个整数 n n n ,表示需要复原的数据的个数。
第二行 n − 1 n-1 n−1 个整数,第 i i i 个数表示 f i + 1 f_{i+1} fi+1 ,即复原第 i + 1 i+1 i+1 个数据需要先复原第 f i + 1 f_{i+1} fi+1 个数据。
接下来 n n n 行,每行两个整数 a i , b i a_i,b_i ai,bi ,表示第 i i i 个数据自身的两个系数。
输出一行,表示最大的代价。
样例输入 1
4
1 1 2
0 0
3 1
5 1
4 1
样例输出 1
14
样例解释 1
可以按照 1 , 2 , 4 , 3 1,2,4,3 1,2,4,3 的方式选择。
样例数据 2,3,4
见下发文件。
本题采用捆绑测试。
对于所有数据,满足 1 ≤ n ≤ 3 × 1 0 5 , 1 ≤ a i , b i ≤ 5000 , 1 ≤ f i < i 1 \le n \le 3\times 10^5,1 \le a_i,b_i \le 5000,1 \le f_i < i 1≤n≤3×105,1≤ai,bi≤5000,1≤fi<i 。
特殊说明:样例数据 1 , 2 1,2 1,2 中出现了 a 1 = b 1 = 0 a_1=b_1=0 a1=b1=0 的情况,但是数据不会出现此情况。
子任务见下表:
子任务编号 | n n n | 特殊性质 | 分值 |
---|---|---|---|
1 1 1 | ≤ 20 \le 20 ≤20 | − - − | 15 15 15 |
2 2 2 | ≤ 1000 \le 1000 ≤1000 | − - − | 15 15 15 |
3 3 3 | ≤ 5 × 1 0 4 \le 5\times 10^4 ≤5×104 | − - − | 15 15 15 |
4 4 4 | ≤ 3 × 1 0 5 \le 3\times 10^5 ≤3×105 | f i = i − 1 f_i=i-1 fi=i−1 | 10 10 10 |
5 5 5 | ≤ 3 × 1 0 5 \le 3\times 10^5 ≤3×105 | f i = 1 f_i=1 fi=1 | 15 15 15 |
6 6 6 | ≤ 3 × 1 0 5 \le 3\times 10^5 ≤3×105 | − - − | 30 30 30 |
后记
自信的小H
最后并没有恢复所有被清除的数据,因此这场模拟赛原本的T1
丢失了,然后有了你们现在看到的这道题。
这道题其实和蓝书上的染色几乎一样,都是倒序操作。
当存在依赖时,即操作必须存在先后,此时考虑如果不存在依赖,则按照
a
i
b
i
\frac{a_i}{b_i}
biai递减选择。
又因为有依赖,所以必须在父亲后,即把点和父亲合并成新点,继续贪心。
#include<bits/stdc++.h>
#define N 300005
typedef long long ll;
using namespace std;
inline ll read(){
ll x=0;char s=getchar();
while(s<'0'||s>'9')s=getchar();
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
return x;
}
int fi[N],fa[N];
inline int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
struct node{
ll sa,sb;
int id,sz;
inline node(int i=0,ll j=0,ll k=0,int h=0){
id=i,sa=j,sb=k,sz=h;
}
bool operator<(const node &x)const{
return sb*x.sa<x.sb*sa;
}
};
priority_queue<node> q;
ll ans;
int sz[N];
ll sa[N],sb[N];
int main(){
// freopen("data3.in","r",stdin);
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
int n=read();ll suma=0,sumb=0;
for(int i=1;i<=n;++i)fa[i]=i,sz[i]=1;
for(int i=2;i<=n;++i){
fi[i]=read();
}
for(int i=1;i<=n;++i){
ll a=read(),b=read();sa[i]=a,sb[i]=b;
suma+=a,sumb+=b;
if(i>1)q.push(node(i,a,b,sz[i]));
}
// cout<<suma<<" "<<sumb<<endl;
while(!q.empty()){
node x=q.top();q.pop();
if(x.sz!=sz[x.id])continue;
int fx=get(fi[x.id]),px=get(x.id);
if(fx==px)continue;
//cout<<px<<endl;
ans+=sb[fx]*sa[px];//printf("%lld %lld\n",sb[fx],sa[px]);
sb[fx]+=sb[px],sa[fx]+=sa[px],sz[fx]+=sz[px];
fa[px]=fx;
if(fx!=1)q.push(node(fx,sa[fx],sb[fx],sz[fx]));
}
printf("%lld\n",ans);
return 0;
}