洛谷P2194HXY烧情侣

传送门啦

这个题可以说是tarjan强连通分量的裸题,但需要维护每个强连通分量的最小值,所以做法就很明确了。

我们先明确几个数组的意思:

1.首先是tarjan缩点中的几个数组:

  dfn[i]:i点的时间戳

  low[i],表示这个点以及其子孙节点连的所有点中dfn最小的值

  stack[],表示当前所有可能能构成是强连通分量的点。

  ins[i],表示 i 是否在stack[ ]数组中
  
  num[i],表示第 i 个强连通分量中有多少个点
  
  belong[i],表示第 i 点在哪一个强连通分量里
  

2.然后是我们用来维护最小值以及最小 值个数的数组

  minn[i],表示第 i 个强连通分量中点权的最小值
  
  sum[]:表示最小值是 i 的有多少个
  
  

最最最需要注意的两初始化:

1.ans2 需要初始化为 1

2.minn[i]这个数组要初始为正无穷。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const long long mod = 1e9 + 7;
const int maxn = 1e5 + 5;
const int maxm = 3e5 + 4;

int n,m,a[maxn],x,y;
struct Edge{
    int to,next,val;
}edge[maxm << 1];
int head[maxn],tot;
int dfn[maxn],low[maxn],ind;
int stack[maxn],top,cnt,num[maxn],belong[maxn];
bool ins[maxn];
int ans1,ans2 = 1,minn[maxn],sum[maxn];

void add(int u,int v){
    edge[++tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot;
}

inline int read(){
    char ch = getchar();
    int f = 1 , x = 0;
    while(ch > '9' || ch < '0'){
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch <= '9' && ch >= '0'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

void print(int x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)  print(x / 10);
    putchar(x % 10 + '0');
}

void tarjan(int x){
    dfn[x] = low[x] = ++ind;
    ins[x] = true;
    stack[++top] = x;
    for(int i=head[x];i;i=edge[i].next){
        int v = edge[i].to;
        if(ins[v])  low[x] = min(low[x] , dfn[v]);
        if(!dfn[v]){
            tarjan(v);
            low[x] = min(low[x] , low[v]);
        }
    }
    int k;
    if(dfn[x] == low[x]){
        ++cnt;
        do{
            k = stack[top];
            num[cnt]++;
            ins[k] = false;
            minn[cnt] = min(minn[cnt] , a[k]);
            top--;
            belong[k] = cnt;
        }  while(k != x);
    }
}

int main(){
    n = read();
    for(int i=1;i<=n;i++){
        a[i] = read();
    }
    m = read();
    for(int i=1;i<=m;i++){
        x = read();  y = read();
        add(x,y);
    }
    for(int i=1;i<=n;i++)
       minn[i] = 1e9;
    for(int i=1;i<=n;i++)
      if(!dfn[i]) tarjan(i);
    for(int i=1;i<=cnt;i++){
        ans1 += minn[i];
    }
    print(ans1);
    cout<<" ";
    for(int i=1;i<=n;i++)
      if(a[i] == minn[belong[i]])
        sum[belong[i]]++;
    for(int i=1;i<=cnt;i++)
      ans2 = ans2 * sum[i] % mod;
    print(ans2);
    return 0;
}

转载于:https://www.cnblogs.com/Stephen-F/p/9884651.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值