题
芬特船长参与了另一个寻宝行动,但只发现了一个奇怪的问题。这个问题可能与宝藏的位置有关,也可能与此无关。这就是为什么弗林特船长决定把解决这个问题的任务留给他的船员,并给了他们一个高得离谱的奖励:一天假。
有两个数组a和b的长度n。初始ans = 0,定义如下操作:
1.选择位置i(1≤i≤n);
2.在ans中加上a[i];
3.如果b[i]≠- 1,则将a[b[i]]中加上a[i]。
对每个i(1≤i≤n)执行一次操作,可以得到的最大ans值是多少?
Bogdan叔叔渴望得到奖励,所以他请求你帮他找到最优的位置顺序来对它们进行操作。
Input
第一行包含整数n(1≤n≤2⋅105)——数组的长度a和b。
第二行包含n整数a1,a2,…,an(−106≤ai≤106)。
第三行包含n整数b1,b2,…,bn(1≤bi≤n或bi=−1)。
附加约束:保证对于任意i(1≤i≤n),序列b[i],b[b[i]],b[b[b[i]]],… 都不是循环的,即总是以−1结束。
Output
在第一行中,打印您可以获得的最大ans字体。
在第二行中,打印操作顺序:nn不同的整数p1,p2,…,pn(1≤pi≤n)。pi是在第i步应该选择的位置。如果有多个订单,打印其中任何一个。
Examples
Input
3
1 2 3
2 3 -1
Output
10
1 2 3
Input
2
-1 100
2 -1
Output
99
2 1
Input
10
-10 -1 2 2 5 -2 -3 -4 2 -6
-1 -1 2 2 -1 5 5 7 7 9
Output
-9
3 5 6 1 9 4 10 7 8 2
第一个样例: 1 + (1 + 2) + (3 + 3) = 10
代码
//博客,拓扑排序
#include "stdio.h"
#include "algorithm"
#include "iostream"
#include "string.h"
#include "vector"
#include "queue"
using namespace std;
int n,m;
long long sum=0;
long long a[200009],b[200009],c[200009];
//c[i]录编号为i的入度
int main()
{
queue<int>q;//记录入度为零的编号
vector<int>good,bad;
//good数组记录a数组中值大于0(正数的编号)的编号
//bad 数组记录a叔祖中值小于等于0(负数的编号)的编号
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i];
for(int i=1; i<=n; i++)
{
cin>>b[i];
if(b[i]!=-1)
c[b[i]]++;
//c[b[i]记录编号为b[i]的入度
}
for(int i=1; i<=n; i++)
{
if(c[i]==0)
q.push(i);
}
while(!q.empty())
{
int u=q.front();
q.pop();
sum+=a[u];
if(a[u]>0)
{
good.push_back(u);
a[b[u]]+=a[u];
c[b[u]]--;
}
else
{
bad.push_back(u);
c[b[u]]--;
}
if(c[b[u]]==0)
q.push(b[u]);
}
cout<<sum<<endl;
for(int i=0; i<good.size(); i++)//正着来使一些正数可以留后代 (会加到其他数上再加到ans(sum)中)
cout<<good[i]<<' ';
for(int i=bad.size()-1; i>=0; i--)//倒着来使负数断子绝孙 (不会加到其他数上再加到ans(sum)中)
cout<<bad[i]<<' ';
}
//为什么先输入正数的编号,再输入负数的编号
//就是把上面的55-58行写成 (交换一下顺序)
/*
for(int i=bad.size()-1; i>=0; i--)
cout<<bad[i]<<' ';
for(int i=0; i<good.size(); i++)
cout<<good[i]<<' ';
*/
/*
例1 1 -5 4
-1 1 2
(先正后负)ans(max)=4+1+(-5+4)=4 顺序3 1 2
(先负后正)ans=-5+4+(1-5)=-5 顺序 2 3 1
例2 1 -5 9
-1 1 2
(先正后负)ans(max)=9+1+(-5+9)=14 顺序3 1 2
(先负后正)ans=-5+9+(1-5)=0 顺序 2 3 1
我们发现:
先正后负可以令某些负数变大(-5-->-1)或变为正数(-5-->4)
先负后正可以另某些正数变小或变为负数
*/