【HNOI 2018】排列

Problem

Description

给定 n n n 个整数 a 1 , a 2 , … , a n ( 0 ≤ a i ≤ n ) a_1, a_2, \ldots , a_n(0 \le a_i \le n) a1,a2,,an(0ain),以及 n n n 个整数 w 1 , w 2 , … , w n w_1, w_2, …, w_n w1,w2,,wn。称 a 1 , a 2 , … , a n a_1, a_2, \ldots , a_n a1,a2,,an 的一个排列 a p [ 1 ] , a p [ 2 ] , … , a p [ n ] a_{p[1]}, a_{p[2]}, \ldots , a_{p[n]} ap[1],ap[2],,ap[n] a 1 , a 2 , … , a n a_1, a_2, \ldots , a_n a1,a2,,an 的一个合法排列,当且仅当该排列满足:对于任意的 k k k 和任意的 j j j,如果 j ≤ k j \le k jk,那么 a p [ j ] a_{p[j]} ap[j] 不等于 p [ k ] p[k] p[k]。(换句话说就是:对于任意的 k k k 和任意的 j j j,如果 p [ k ] p[k] p[k] 等于 a p [ j ] a_{p[j]} ap[j],那么 k &lt; j k&lt;j k<j。)

定义这个合法排列的权值为 w p [ 1 ] + 2 w p [ 2 ] + … + n w p [ n ] w_{p[1]} + 2w_{p[2]} + \ldots + nw_{p[n]} wp[1]+2wp[2]++nwp[n]。你需要求出在所有合法排列中的最大权值。如果不存在合法排列,输出 − 1 -1 1

样例解释中给出了合法排列和非法排列的实例。

Input Format

第一行一个整数 n n n

接下来一行 n n n 个整数,表示 a 1 , a 2 , … , a n a_1,a_2,\ldots , a_n a1,a2,,an

接下来一行 n n n 个整数,表示 w 1 , w 2 , … , w n w_1,w_2,\ldots ,w_n w1,w2,,wn

Output Format

输出一个整数表示答案。

Sample

Input 1

3
0 1 1
5 7 3

Output 1

32

Input 2

3
2 3 1
1 2 3

Output 2

-1

Input 3

10
6 6 10 1 7 0 0 1 7 7
16 3 10 20 5 14 17 17 16 13

Output 3

809

Explanation

Explanation for Input 1

对于 a 1 = 0 , a 2 = 1 , a 3 = 1 a_1=0,a_2=1,a_3=1 a1=0,a2=1,a3=1,其排列有

  • a 1 = 0 , a 2 = 1 , a 3 = 1 a_1=0,a_2=1,a_3=1 a1=0,a2=1,a3=1,是合法排列,排列的权值是 1 ∗ 5 + 2 ∗ 7 + 3 ∗ 3 = 28 1*5+2*7+3*3=28 15+27+33=28
  • a 2 = 1 , a 1 = 0 , a 3 = 1 a_2=1,a_1=0,a_3=1 a2=1,a1=0,a3=1,是非法排列,因为 a p [ 1 ] a_{p[1]} ap[1] 等于 p [ 2 ] p[2] p[2]
  • a 1 = 0 , a 3 = 1 , a 2 = 1 a_1=0,a_3=1,a_2=1 a1=0,a3=1,a2=1,是合法排列,排列的权值是 1 ∗ 5 + 2 ∗ 3 + 3 ∗ 7 = 32 1*5+2*3+3*7=32 15+23+37=32
  • a 3 = 1 , a 1 = 0 , a 2 = 1 a_3=1,a_1=0,a_2=1 a3=1,a1=0,a2=1,是非法排列,因为 a p [ 1 ] a_{p[1]} ap[1] 等于 p [ 2 ] p[2] p[2]
  • a 2 = 1 , a 3 = 1 , a 1 = 0 a_2=1,a_3=1,a_1=0 a2=1,a3=1,a1=0,是非法排列,因为 a p [ 1 ] a_{p[1]} ap[1] 等于 p [ 3 ] p[3] p[3]
  • a 3 = 1 , a 2 = 1 , a 1 = 0 a_3=1,a_2=1,a_1=0 a3=1,a2=1,a1=0,是非法排列,因为 a p [ 1 ] a_{p[1]} ap[1] 等于 p [ 3 ] p[3] p[3]

因此该题输出最大权值 32 32 32

Explanation for Input 2

对于 a 1 = 2 , a 2 = 3 , a 3 = 1 a_1=2,a_2=3,a_3=1 a1=2,a2=3,a3=1,其排列有:

  • a 1 = 2 , a 2 = 3 , a 3 = 1 a_1=2,a_2=3,a_3=1 a1=2,a2=3,a3=1,是非法排列,因为 a p [ 1 ] a_{p[1]} ap[1] 等于 p [ 2 ] p[2] p[2]
  • a 2 = 3 , a 1 = 2 , a 3 = 1 a_2=3,a_1=2,a_3=1 a2=3,a1=2,a3=1,是非法排列,因为 a p [ 1 ] a_{p[1]} ap[1] 等于 p [ 3 ] p[3] p[3]
  • a 1 = 2 , a 3 = 1 , a 2 = 3 a_1=2,a_3=1,a_2=3 a1=2,a3=1,a2=3,是非法排列,因为 a p [ 1 ] a_{p[1]} ap[1] 等于 p [ 3 ] p[3] p[3]
  • a 3 = 1 , a 1 = 2 , a 2 = 3 a_3=1,a_1=2,a_2=3 a3=1,a1=2,a2=3,是非法排列,因为 a p [ 2 ] a_{p[2]} ap[2] 等于 p [ 3 ] p[3] p[3]
  • a 2 = 3 , a 3 = 1 , a 1 = 2 a_2=3,a_3=1,a_1=2 a2=3,a3=1,a1=2,是非法排列,因为 a p [ 2 ] a_{p[2]} ap[2] 等于 p [ 3 ] p[3] p[3]
  • a 3 = 1 , a 2 = 3 , a 1 = 2 a_3=1,a_2=3,a_1=2 a3=1,a2=3,a1=2,是非法排列,因为 a p [ 1 ] a_{p[1]} ap[1] 等于 p [ 3 ] p[3] p[3]

因此该题没有合法排列。

Range

对于前 20 % 20\% 20% 的数据, 1 ≤ n ≤ 10 1 \le n \le 10 1n10

对于前 40 % 40\% 40% 的数据, 1 ≤ n ≤ 15 1 \le n \le 15 1n15

对于前 60 % 60\% 60% 的数据, 1 ≤ n ≤ 1000 1 \le n \le 1000 1n1000

对于前 80 % 80\% 80% 的数据, 1 ≤ n ≤ 100000 1 \le n \le 100000 1n100000

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 500000 1 \le n \le 500000 1n500000 0 ≤ a i ≤ n ( 1 ≤ i ≤ n ) 0 \le a_i \le n (1 \le i \le n) 0ain(1in) 1 ≤ w i ≤ 1 0 9 1 \le w_i \le 10^9 1wi109 ,所有 w i w_i wi 的和不超过 1.5 × 1 0 13 1.5 \times 10^{13} 1.5×1013

Algorithm

并查集

Mentality

这一题的题面很神仙,略难懂。

简而言之,有一棵树,告诉你每个节点的父亲和权值,只有选择了父亲才能选择儿子,如果当前选到的节点是你选的第 i i i 个,那么它的贡献为 i × i× i×权值 。问最大的总贡献。必须选完整棵树。

那么数据里不能有环,否则输出 − 1 -1 1

除去无解的情况,我们可以开始想想怎么做了:

首先想到一个很显然的错误贪心,那就是用一个优先队列维护当前能选的所有点,优先选权值最小的,不过这很好卡,若 i i i 的权值为 1 e 9 1e9 1e9 ,其他所有节点的权值为 1 e 9 − 1 1e9-1 1e91 i i i 的子树权值都为 1 1 1 。那这个贪心就死掉了。

但是不打紧,我们可以考虑这样一些东西:对于一个节点 i i i ,如果它的权值是最小的,那么当我们选完了 f a [ i ] fa[i] fa[i] ,我们必定会选择 i i i ,也就是说,对于权值最小的 i i i ,它一定会在 f a [ i ] fa[i] fa[i] 之后挨着选择。

所以我们可以考虑将最后答案的选择序列分割为 n n n 块,利用这个贪心一块块按顺序合并。

但是,对于两块 s i z e &gt; 1 size&gt;1 size>1 的序列,我们如何合并它们呢?

假设我已经选到了第 i i i 位,之后要按顺序合并 a a a b b b 两块序列。

W i W_i Wi 为序列 i i i 内元素权值和, w i j w_{i_j} wij 代表序列 i i i 内第 j j j 个元素的权值。

那么如果我们先选 a a a 再选 b b b ,贡献就会是这样的:

∑ j = 1 s i z e a ( i + j ) ⋅ w a j + ∑ j = 1 s i z e b ( i + s i z e a + j ) ⋅ w b j \sum_{j=1}^{size_a}(i+j)\cdot w_{a_j}+\sum_{j=1}^{size_b}(i+size_a+j)\cdot w_{b_j} j=1sizea(i+j)waj+j=1sizeb(i+sizea+j)wbj

而先选 b b b 的话,贡献如下:

∑ j = 1 s i z e b ( i + j ) ⋅ w b j + ∑ j = 1 s i z e a ( i + s i z e b + j ) ⋅ w a j \sum_{j=1}^{size_b}(i+j)\cdot w_{b_j}+\sum_{j=1}^{size_a}(i+size_b+j)\cdot w_{a_j} j=1sizeb(i+j)wbj+j=1sizea(i+sizeb+j)waj

则先选 a a a 更优,当且仅当 s i z e a ∗ s u m b &gt; s i z e b ∗ s u m a size_a*sum_b&gt;size_b*sum_a sizeasumb>sizebsuma

那么贪心策略便彻底确定下来了,我们只需要按证明出的结论的优劣性贪心选择并合并即可。

过程如下:

  • 初始化并查集
  • 选择堆顶元素,并检查元素是否为所在集合的根
  • 合并当前元素与父亲所在集合,并计算对答案的贡献

完毕。

Code

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int n,cnt,Fa[500001],head[500001],nx[500001],to[500001];
int fa[500001],size[500001];
long long w[500001],ans;
bool vis[500001],flag;
struct node
{
	int x,size;
	long long w;
	bool operator<(const node b)const{return w*b.size>b.w*size;}//定义比较函数
};
priority_queue <node> q;
int find(int x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void Push(int x)
{
	q.push(node{x,size[x],w[x]});
}
void Merge(int f,int x)
{
	ans+=1ll*size[f]*w[x];//计算贡献
	w[f]+=w[x];
	size[f]+=size[x];
}//合并集合
void dfs(int x)
{
	if(flag)return;
	if(vis[x])flag=1;
	vis[x]=1,cnt++;
	for(int i=head[x];i;i=nx[i])
		dfs(to[i]);
}
int main()
{
	freopen("4437.in","r",stdin);
	freopen("4437.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&Fa[i]);
		nx[i]=head[Fa[i]],to[i]=i;
		head[Fa[i]]=i;
	}
	dfs(0);
	if(cnt<n||flag)
	{
		cout<<"-1";
		return 0;
	}//判断不合法情况--环
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&w[i]);
		ans+=w[i];
		fa[i]=i,size[i]=1;
		Push(i);
	}//初始化并查集与答案
	int x,f;
	while(!q.empty())
	{
		x=q.top().x;
		q.pop();
		if(x!=find(x))continue;//判断是否为并查集根部元素
		x=find(x);
		fa[x]=f=find(Fa[x]);//合并
		Merge(f,x);
		if(f)Push(f);//加入堆内
	}
	cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值