Tarjan(强连通分量缩点) - Mouse Hunt - CodeForces 1027D

Tarjan(强连通分量缩点) - Mouse Hunt - CodeForces 1027D

题意:

给 定 一 个 n 个 点 , n 条 边 的 有 向 图 , 给定一个n个点,n条边的有向图, nn

初 始 时 , 老 鼠 可 能 在 任 意 点 , 初始时,老鼠可能在任意点,

接 着 老 鼠 将 从 点 i 到 点 a i , 接着老鼠将从点i到点a_i, iai

在 i 点 放 置 一 个 捕 捉 装 置 需 要 花 费 c i , 在i点放置一个捕捉装置需要花费c_i, ici

计 算 最 少 花 费 , 能 够 确 保 抓 住 老 鼠 ( 无 论 老 鼠 从 什 么 位 置 出 发 ) 。 计算最少花费,能够确保抓住老鼠(无论老鼠从什么位置出发)。 ()

输入:

首 行 一 个 正 整 数 n , 首行一个正整数n, n

接 着 n 个 正 整 数 , c 1 , c 2 , . . . , c n 接着n个正整数,c_1,c_2,...,c_n nc1,c2,...,cn

最 后 一 行 为 n 个 正 整 数 : a 1 , a 2 , . . . , a n 最后一行为n个正整数:a_1,a_2,...,a_n na1,a2,...,an

输出:

一 个 正 整 数 , 表 示 最 少 花 费 。 一个正整数,表示最少花费。

Examples
Input

5
1 2 3 2 10
1 3 4 3 3

Output

3

Input

4
1 10 2 10
2 4 2 2

Output

10

Input

7
1 1 1 1 1 1 1
2 2 2 3 6 7 6

Output

2

数据范围:

1 ≤ n ≤ 2 ⋅ 1 0 5 , 1 ≤ c i ≤ 1 0 4 , 1 ≤ a i ≤ n 1≤n≤2⋅10^5,1≤c_i≤10^4,1≤a_i≤n 1n21051ci1041ain


分析:

根 据 题 意 , 我 们 在 i 和 a i 之 间 连 接 一 条 有 向 边 , 每 个 点 有 且 仅 有 一 条 出 边 。 根据题意,我们在i和a_i之间连接一条有向边,每个点有且仅有一条出边。 iai

要 能 确 保 抓 住 老 鼠 , 事 实 上 , 我 们 没 有 必 要 在 入 度 为 0 的 点 上 放 置 捕 捉 装 置 , 要能确保抓住老鼠,事实上,我们没有必要在入度为0的点上放置捕捉装置, 0

因 为 该 点 必 然 会 到 达 其 他 点 , 我 们 在 其 能 够 到 达 的 其 他 点 上 放 捕 捉 装 置 也 是 可 行 的 。 因为该点必然会到达其他点,我们在其能够到达的其他点上放捕捉装置也是可行的。

而 且 , 我 们 必 须 在 出 度 为 0 的 点 上 放 置 一 个 捕 捉 装 置 。 而且,我们必须在出度为0的点上放置一个捕捉装置。 0

首 先 我 们 通 过 t a r j a n 算 法 对 强 连 通 分 量 进 行 缩 点 , 首先我们通过tarjan算法对强连通分量进行缩点, tarjan

每 个 强 连 通 分 量 的 权 值 为 该 强 连 通 分 量 内 部 所 有 点 的 权 值 的 最 小 值 。 每个强连通分量的权值为该强连通分量内部所有点的权值的最小值。

接 着 统 计 每 一 个 连 通 分 量 的 出 度 , 接着统计每一个连通分量的出度,

最 后 , 我 们 将 所 有 出 度 为 0 的 点 的 权 值 累 加 上 就 是 最 小 花 费 。 最后,我们将所有出度为0的点的权值累加上就是最小花费。 0

注 意 , 自 环 的 情 况 需 要 特 判 , 即 i = a i 的 情 况 , 需 要 在 该 点 上 也 放 置 一 个 装 置 。 注意,自环的情况需要特判,即i=a_i的情况,需要在该点上也放置一个装置。 i=ai

代码:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>

#define ll long long

using namespace std;

const int N=200010, M=200010;

int n,m;
int e[M],ne[M],h[N],w[N],idx;
int stk[N],top;
bool in_stk[N];
int id[N],ssc_cnt;
int dfn[N],low[N],timestamp;
int minp[N],dout[N];
int a[N];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void tarjan(int u)
{
    dfn[u]=low[u]=++timestamp;
    stk[++top]=u,in_stk[u]=true;
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(!dfn[j])
        {
            tarjan(j);
            low[u]=min(low[u],low[j]);
        }
        else if(in_stk[j]) low[u]=min(low[u],dfn[j]);
    }

    if(dfn[u]==low[u])
    {
        ++ssc_cnt;
        int y;
        do
        {
            y=stk[top--];
            in_stk[y]=false;
            id[y]=ssc_cnt;
            minp[ssc_cnt]=min(minp[ssc_cnt],w[y]);
        }while(y!=u);
    }
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        add(i,a[i]);
    }
    
    memset(minp,0x3f,sizeof minp);
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i);
        
    for(int i=1;i<=n;i++)
        for(int j=h[i];~j;j=ne[j])
        {
            int k=e[j];
            int u=id[i], v=id[k];
            if(a[i]==i || u==v) continue;
            dout[u]++;
        }

    int res=0;
    for(int i=1;i<=ssc_cnt;i++)
        if(!dout[i])
            res+=minp[i];

    cout<<res<<endl;
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值