加载中...

在强连通图里 求最小值方案最小数

https://www.luogu.com.cn/problem/P2194

对每个连通块的值求里面的点的最小值 然后加起来就是遍历所有点的最小值
方案数就根据乘法原理乘起来就可以

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
unordered_map<string,int>cou;
const int N =100005, M=3e5+10;
int n,m;
int h[N], e[M], ne[M], idx;
int dfn[N],low[N],timestamp;
int stk[N],top;
bool in_stk[N];
int id[N],scc_cnt;
int din[N],dout[N];
int w[N]; 
int minn[N],sum[N];
void add(int a, int b)  // 添加一条边a->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]){
        ++scc_cnt;
        int y;
        do{
            y=stk[top--];
            in_stk[y]=false;
            id[y]=scc_cnt;
            //如果和minn的值相等 
            if(minn[scc_cnt]==w[y]){//这两个if顺序不能调转 
            	sum[scc_cnt]++;
			} 
			//如果比minn的值小 
            if(w[y]<minn[scc_cnt]){
				minn[scc_cnt]=w[y];
				sum[scc_cnt]=1;
			}
           
        }while(y!=u);
    }
}
int main()
{
    cin>>n;
    memset(h, -1, sizeof h);
    memset(minn,0x3f,sizeof(minn));//注意minn表示连通块的值最小需要初始化的大 才会被更新 
    for(int i=1;i<=n;i++){
    	cin>>w[i];
	}
    int x,y;
    cin>>m;
    for (int i = 1; i <= m; i ++ ){
        cin>>x>>y;
        add( x,y );//这里反向边指回来 
    }
    
    for (int i = 1; i <= n; i ++ )//tarjan 遍历所有点 所以是2*n 
    if(!dfn[i])
    tarjan(i);
    long long  ans1=0,ans2=1;//ans1是最小值 ans2是方案书 
    for(int i=1;i<=scc_cnt;i++){
    	ans1=ans1+minn[i];
    	ans2=(ans2*sum[i])%1000000007; 
	}

	cout<<ans1<<" "<<ans2<<endl;

    return 0;
}
posted @ 2022-09-01 16:59  liang302  阅读(45)  评论(0编辑  收藏  举报