题目:http://codeforces.com/problemset/problem/427/C
题意:给你n座城市,m条有向道路,然后有一个机制,你在某一个城市设置检查点,那么被设置的检查点受保护还有通过这个点所连着的边走出去后面还能走回来的点,也就是这两个点能互相到达,那么
那个点也受到保护,每个城市设置检查点有一个权值,问你权值最小,并输出那个方案的个数有多少个
思路:我们既然是求两个点能互相到达,这其实有个名词叫强连通,如果两个点满足强连通的话,那么他们之间就只用选一个点即可,所以我们就知道,
我们用tarjan求强连通分量,然后在每个分量里面选最小的那个值累加即可,方案数的话,我们每个分量里面最优是取最小值才是最优的,所以我们求每个分量里面最小值的个数有多少个,然后累乘
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <map> #define mod 1000000007 using namespace std; const int MAXN = 310000; struct Node { int next1,c; }edge[MAXN]; int head[MAXN]; int dfn[MAXN],low[MAXN]; int vis[MAXN],stact[MAXN]; int tot,cnt,index; int a[MAXN]; long long flag=1; long long sum=0; void add(int xx,int y) //前向星 { edge[++cnt].next1 = head[xx]; edge[cnt].c = y; head[xx] = cnt; } void Tarjan(int x) { dfn[x] = low[x] = ++tot; //这里也不可以是0,因为0表示还没有开始深搜,要从1开始 vis[x] = 1;stact[++index] = x;//注意这里的stact中的0位置是什么都不能储存的,因为后面要去判断栈中直到某一个数全部输出,所以要到达他的后一个位置,所以不能是0 for(int i = head[x];i != -1; i = edge[i].next1)//找到这个点的全部变 { int v = edge[i].c; if(!dfn[v]) //如果还没有深搜过这个点 { Tarjan(v); low[x] = min(low[x],low[v]); } else if(vis[v]) //如果这个点已经在栈中了,那么正在遍历的这个点的low就要指向他的父亲节点,也就是小的那个 low[x] = min(low[x],dfn[v]); //比较谁是谁的儿子 } if(low[x] == dfn[x]) //表示在x之后在都在一个连通分量里面,所以要全部输出 { long long mn=99999999999; map<int,int> mp; do{ //printf("%d ",stact[index]); mn=min(mn,(long long)a[stact[index]]); mp[a[stact[index]]]++; vis[stact[index]] = 0; index -- ; }while(x != stact[index+1]); sum=sum+mn; flag=(flag*mp[mn])%mod; } return ; } int main() { memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(vis,0,sizeof(vis)); memset(stact,0,sizeof(stact)); tot=0;cnt=0;index=0; int n,m; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int xx,y; scanf("%d",&m); for(int i = 1;i <= m;i ++) { scanf("%d%d",&xx,&y); add(xx,y); } for(int i = 1; i <= n;i ++) if(!dfn[i]) Tarjan(i); cout<<sum<<" "<<flag; return 0; }