水晶铭刻之旅【牛客tracker & 每日一题】

本题基于有向图建模水晶与传送门关系,核心是求强连通分量(SCC):每个SCC内可构造回路一次性铭刻所有节点,消耗为该分量内最小激活魔力值;总最小消耗为各SCC最小魔力之和,方案数为各SCC中最小魔力节点个数的乘积(模10^9+7)。采用Tarjan算法线性求解SCC,时间复杂度O(n+m),适用于n≤10^5、m≤3×10^5规模。

水晶铭刻之旅

时间限制:1 秒
空间限制:256M

网页链接

牛客tracker

牛客tracker & 每日一题,完成每日打卡,即可获得牛币。获得相应数量的牛币,能在【牛币兑换中心】,换取相应奖品!助力每日有题做,丰盈牛币日益多!


题目描述

在远古魔法世界中,有 n n n 块蕴含强大力量的魔法水晶,第 i i i 块水晶的激活需要消耗魔力 w i w_i wi​。这些水晶之间由 m m m 条单向传送门相连,第 j j j 条传送门可将咒术师从水晶 x j x_j xj​ 传送到水晶 y j y_j yj​。

咒术师掌握了"回路铭刻术":

  • 若他在某块水晶 u u u 处开启铭刻,并沿着传送门行进,最终能回到 u u u,那么经过的所有水晶都可一次性被铭刻,仅需消耗起点水晶的激活魔力 w u w_u wu​;
  • 咒术师可多次从任意水晶发起回路,直至所有水晶均被铭刻。

请帮助咒术师:

  • 在完成全部水晶铭刻的前提下,将总消耗魔力最小化;
  • 若存在多种消耗相同的最优铭刻方案,计算方案总数,并对 ( 10 9 + 7 ) (10^9 + 7) (109+7) 取模。

【名词解释】

  • 回路:回路 指沿传送门方向行走,最终回到起点的路径,可重复经过同一水晶或传送门。
  • 铭刻方案:方案 指一系列回路操作,使得每块水晶至少在次回路中出现过一次。

输入描述

  • 第一行输入整数 n   ( 1 ≤ n ≤ 10 5 ) n\ (1 \le n \le 10^5) n (1≤n≤105),表示水晶数量;
  • 第二行输入 n n n 个整数 w 1 , w 2 , … , w n   ( 0 ≤ w i ≤ 10 9 ) w_1, w_2, \dots, w_n\ (0 \le w_i \le 10^9) w1​,w2​,…,wn​ (0≤wi​≤109),依次表示每块水晶的激活魔力;
  • 第三行输入整数 m   ( 0 ≤ m ≤ 3 × 10 5 ) m\ (0 \le m \le 3 \times 10^5) m (0≤m≤3×105),表示传送门数量;
  • 接下来 m m m 行,每行输入两个整数 x j , y j   ( 1 ≤ x j , y j ≤ n ) x_j, y_j\ (1 \le x_j, y_j \le n) xj​,yj​ (1≤xj​,yj​≤n),表示存在一条从水晶 x j x_j xj​ 指向水晶 y j y_j yj​ 的单向传送门。

输出描述

输出一行,包含两个整数:

  • 所需的最小总消耗魔力;
  • 在该最小消耗下的可行铭刻方案数对 ( 10 9 + 7 ) (10^9 + 7) (109+7) 取模后的结果。

示例 1

输入:

3
1 2 3
3
1 2
2 3
3 2

输出:

3 1

说明: 示例中,水晶 { 2 , 3 } \{2,3\} {2,3} 构成回路,可选起点魔力为 min ⁡ ( 2 , 3 ) = 2 \min(2,3) = 2 min(2,3)=2;水晶 1 1 1 独立,付费 1 1 1。总魔力 1 + 2 = 3 1 + 2 = 3 1+2=3,方案数 1 × 1 = 1 1 \times 1 = 1 1×1=1。


示例 2

输入:

3
10 20 10
4
1 2
1 3
3 1
2 1

输出:

10 2

说明: 示例中,所有水晶互联可形成回路,回路中最小激活魔力为 10 10 10(可选水晶 1 1 1 或 3 3 3),故方案数为 2 2 2。


数据范围

  • 1 ≤ n ≤ 10 5 1 \le n \le 10^5 1≤n≤105
  • 0 ≤ w i ≤ 10 9 0 \le w_i \le 10^9 0≤wi​≤109
  • 0 ≤ m ≤ 3 × 10 5 0 \le m \le 3 \times 10^5 0≤m≤3×105
  • 1 ≤ x j , y j ≤ n 1 \le x_j, y_j \le n 1≤xj​,yj​≤n

解题思路

本题是强连通分量缩点 + 贪心计数的经典问题。给定一张有向图,每个节点有一个激活魔力值,一次“回路铭刻”可以从某个节点出发,沿着有向边行走并最终回到起点,路径上经过的所有节点均可被一次性铭刻,总消耗仅为起点节点的激活魔力。要求用若干次回路铭刻覆盖所有节点,最小化总消耗,并统计达到最小消耗的方案数。

1. 问题等价转化
  • 强连通分量内部可一次覆盖:在有向图中,若若干节点构成一个强连通分量(SCC),则从其中任意一个节点出发,都可以沿着有向边遍历该分量内的所有节点并回到起点(允许重复经过节点和边)。因此,一个强连通分量内的所有节点可以通过一次回路铭刻全部覆盖,且这次铭刻的消耗仅由所选起点的魔力值决定。
  • 不同强连通分量相互独立:缩点后,SCC之间不存在回路,因此每个SCC必须至少发起一次回路铭刻。不同SCC的回路选择互不影响,总消耗为各SCC最小消耗之和,方案数为各SCC方案数的乘积。
  • 单个SCC的最优选择:对于每个SCC,为了使消耗最小,应选择该SCC内激活魔力最小的节点作为起点。若存在多个节点具有相同的最小魔力值,则任选其一作为起点均可达到最优,因此该SCC的方案数等于最小魔力值节点的个数。
2. 算法实现
  1. 建图:读入节点数 n n n、每个节点的魔力值 w i w_i wi​、边数 m m m 以及 m m m 条有向边,使用链式前向星存储。
  2. Tarjan 求强连通分量:
    • 使用 dfn 和 low 数组进行深度优先搜索,维护一个栈 stk 记录当前搜索路径上的节点。
    • 当 dfn[u] == low[u] 时,说明找到了一个强连通分量,从栈中弹出节点直到 u u u,为这些节点分配同一个 SCC 编号。
    • 在弹出节点的过程中,统计该 SCC 内的最小魔力值 minCost 以及达到该最小值的节点个数 minCnt。
  3. 统计答案:
    • 遍历所有 SCC,将每个 SCC 的 minCost 累加到总消耗 sum 中。
    • 将每个 SCC 的 minCnt 累乘到方案数 ways 中(对 10 9 + 7 10^9+7 109+7 取模)。
  4. 输出:输出总消耗 sum 和方案数 ways。
3. 复杂度分析
  • 时间复杂度:Tarjan 算法需要遍历所有节点和边一次,时间复杂度为 O ( n + m ) O(n+m) O(n+m)。 n ≤ 10 5 n \le 10^5 n≤105, m ≤ 3 × 10 5 m \le 3 \times 10^5 m≤3×105,完全可行。
  • 空间复杂度:需要存储邻接表、dfn、low、栈、SCC 信息等,空间复杂度为 O ( n + m ) O(n+m) O(n+m),满足 256M 限制。

总结

利用强连通分量将图缩点,每个 SCC 内部可以通过一次回路铭刻完全覆盖,且选择其中魔力最小的节点作为起点即可使该分量消耗最小。各 SCC 相互独立,总消耗为各分量最小魔力之和,方案数为各分量最小魔力节点个数的乘积。Tarjan 算法在线性时间内求出所有 SCC 并统计信息,整体思路清晰且高效。

代码内容

#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef vector<vector<ll>> vvt;
typedef pair<ll,ll> pll;
const ll N=200000+10;
const ll M=1000000+10;
const ll mod=1000000007;
const ll INF=1e9;

ll head[N],to[M],nxt[M],idx;
ll cost[N],dfn[N],low[N],stk[N],sccId[N];
ll minCost[N],minCnt[N];
bool inStk[N];
ll n,m,timerCnt,top,sccCnt;

void addEdge(ll u,ll v)
{
    to[++idx]=v;
    nxt[idx]=head[u];
    head[u]=idx;
}

void tarjan(ll u)
{
    dfn[u]=low[u]=++timerCnt;
    stk[++top]=u;
    inStk[u]=true;
    for(ll i=head[u];i!=0;i=nxt[i])
    {
        ll v=to[i];
        if(dfn[v]==0)
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(inStk[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u])
    {
        sccCnt++;
        minCost[sccCnt]=INF;
        minCnt[sccCnt]=0;
        while(true)
        {
            ll x=stk[top--];
            inStk[x]=false;
            sccId[x]=sccCnt;
            if(cost[x]<minCost[sccCnt])
            {
                minCost[sccCnt]=cost[x];
                minCnt[sccCnt]=1;
            }
            else if(cost[x]==minCost[sccCnt])
            {
                minCnt[sccCnt]++;
            }
            if(x==u) break;
        }
    }
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(ll i=1;i<=n;i++)
    {
        cin>>cost[i];
    }
    cin>>m;
    for(ll i=1;i<=m;i++)
    {
        ll u,v;
        cin>>u>>v;
        addEdge(u,v);
    }
    for(ll i=1;i<=n;i++)
    {
        if(dfn[i]==0)
        {
            tarjan(i);
        }
    }
    ll sum=0;
    ll ways=1;
    for(ll i=1;i<=sccCnt;i++)
    {
        sum+=minCost[i];
        ways=ways*minCnt[i]%mod;
    }
    cout<<sum<<" "<<ways;
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pretty Boy Fox

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值