Codeforces Round #661 (Div. 2) D. Captain Flint and Treasure(DFS或者拓扑排序)

D. Captain Flint and Treasure

题意:

给你一个两个数组。a和b。长度都是n
a存值。
b存指针。

  1. 现在可以进行n次操作。
  2. 每次选取 [ 1 , n ] [1,n] [1,n]里的一个位置 i i i。(每个位置只能被选择一次)
    进行如下
  1. ans+=a【i】
  2. 如果b【i】!=-1. 那么把a【b【i】】+=a【i】。
  1. 问:怎样使得ans最大。

思路:

  1. 可以把a和b之间的关系抽象成图。
  2. 在读入b时,可以连边(当b【i】!=-1)add(b【i】,i)。
  3. 那么接下来考虑怎样最优。
  1. 假如a【now】>0,那么把now这个位置放在前面操作,对答案的贡献越大。
    它可以对a【b【now】】+=a【now】。(pre)并放入pre_order里。
  2. 假如a【now】<0,那么就不能让它影响后面(a【b【now】】+=a【now】)。并放入post_order里。
  3. 显然,对于a【】>0 和 a【】<0 分别定义pre_order和post_order去存储答案。
  1. 最后写dfs去实现。
  2. pre_order先输出(正值先操作,)
  3. post_order后输出且是倒序(负值最后才操作)。

写法:

o(n)可以dfs。
o(nlogn)可以拓扑。

反思

写好 D F S DFS DFS.(根据问题,对自己问几个why)

  1. 什么时候要去叶子?
  2. 回溯后(递归后)改变值?
  3. 还是递归前改变值?

AC(O(n))

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#define fzhead EDGE(int _to, int _v, int _next)
#define fzbody to(_to), v(_v), next(_next)
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for (register int i=(x);i<=(y);i++)
#define FOR(i,x,y) for (register int i=(x);i<=(y);i++)
#define mp make_pair
#define fi first
#define se second
#define pb push_back
#define sz(a) (int)(a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef pair<int, int>pa;
typedef pair<ll,ll>PA;
inline int read()
{
    int ans=0;
    char c=getchar();bool neg=false;
    while(c<'0'||c>'9')
    {
        if(c=='-')neg=true;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        ans=ans*10+c-'0';
        c=getchar();
    }
    return (neg)?-ans:ans;
}
void write(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
const int maxn=2e5+10;
int head[maxn<<1], dis[maxn], vis[maxn];
int cnt,n,m;
struct EDGE
{
    int to,v,next;
    EDGE(){}
    fzhead:fzbody{}
}e[maxn<<1];
void add(int bg, int ed, int v)
{
    e[++cnt]=EDGE(ed, v, head[bg]);
    head[bg]=cnt;
}
ll a[maxn],b[maxn],use[maxn];
ll ans=0;
vector<int>pre_order,post_order;
void dfs(int now){
    use[now]=1;
    for(int i=head[now]; i!=-1; i=e[i].next){
        if(!use[e[i].to]){
            dfs(e[i].to);
        }
    }
    ans+=a[now];
    if(b[now]!=-1&&a[now]>0){
        a[b[now]]+=a[now];
    }
    if(a[now]>0)pre_order.pb(now);
    else post_order.pb(now);
}
int main()
{
    n=read();cnt=0;
    for(int i=0; i<=n; i++)head[i]=-1;
    for(int i=1; i<=n; i++)a[i]=read();
    for(int i=1; i<=n; i++)b[i]=read();
    for(int i=1; i<=n; i++){
        if(b[i]!=-1)add(b[i],i,1);//b[i]++
    }
    for(int i=1; i<=n; i++){
        if(!use[i])dfs(i);
    }
    reverse(all(post_order));
    cout<<ans<<endl;
    for(int i=0; i<sz(pre_order); i++)cout<<pre_order[i]<<' ';
    for(int i=0; i<sz(post_order); i++)cout<<post_order[i]<<' ';
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值