水晶铭刻之旅
时间限制: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. 算法实现
- 建图:读入节点数 n n n、每个节点的魔力值 w i w_i wi、边数 m m m 以及 m m m 条有向边,使用链式前向星存储。
- Tarjan 求强连通分量:
- 使用
dfn和low数组进行深度优先搜索,维护一个栈stk记录当前搜索路径上的节点。 - 当
dfn[u] == low[u]时,说明找到了一个强连通分量,从栈中弹出节点直到 u u u,为这些节点分配同一个 SCC 编号。 - 在弹出节点的过程中,统计该 SCC 内的最小魔力值
minCost以及达到该最小值的节点个数minCnt。
- 使用
- 统计答案:
- 遍历所有 SCC,将每个 SCC 的
minCost累加到总消耗sum中。 - 将每个 SCC 的
minCnt累乘到方案数ways中(对 10 9 + 7 10^9+7 109+7 取模)。
- 遍历所有 SCC,将每个 SCC 的
- 输出:输出总消耗
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;
}
本题基于有向图建模水晶与传送门关系,核心是求强连通分量(SCC):每个SCC内可构造回路一次性铭刻所有节点,消耗为该分量内最小激活魔力值;总最小消耗为各SCC最小魔力之和,方案数为各SCC中最小魔力节点个数的乘积(模10^9+7)。采用Tarjan算法线性求解SCC,时间复杂度O(n+m),适用于n≤10^5、m≤3×10^5规模。
3311

被折叠的 条评论
为什么被折叠?



