强连通图,Tarjan——CodeForces - 427C
题目含义
给出一个图,每个强连通图都要寻找一个点
要求寻找的点的价值之和最少,并且问这个最低价值有几种选法
题目分析
使用Tarjan算法,每次找到一个强连通图时出栈,并在出栈过程寻找最低价值的点和这个点的个数
最后把每个强连通图的最低价值加起来,个数都相乘就得到最后答案
有一个需要注意的点——
平时都可以把instack[x]=0写在if(dfn[x]==low[x])前面,因为当改变了x的instack后,其他位置就无法通过访问x访问x的子树,但是这道题我用了一个dfs访问每一个可能存在的强连通图找答案,后寻找的点可能是前面寻找过的点的子树点,前面没还原的instack可能会影响后面的值
所以要么把instack写在退栈循环里,要么给dfs加上一个初始化instack=0
别问我为什么instack=0不能写在if前面
题目代码
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; typedef long long LL; const int maxn=1e5+7; const int mod=1e9+7; const int INF=0x3f3f3f3f; struct edge{ int to,next; }e[maxn*3]; int head[maxn],minc[maxn],nway[maxn],st[maxn],instack[maxn],low[maxn],dfn[maxn],cost[maxn]; int n,m,a,b,top,cnt,scc,tot; void add(int u,int v){ e[tot].to=v; e[tot].next=head[u]; head[u]=tot++; } void init(){ memset(head,-1,sizeof(head)); } void Tarjan(int x){ st[++top]=x; instack[x]=1; low[x]=dfn[x]=++cnt; for(int i=head[x];i!=-1;i=e[i].next){ int y=e[i].to; if(!dfn[y]){ Tarjan(y); low[x]=min(low[x],low[y]); } else if(instack[y])low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ int p=-1; scc++; minc[scc]=INF; do{ p=st[top]; instack[p]=0; if(cost[p]<minc[scc]){ minc[scc]=cost[p]; nway[scc]=1; } else if(cost[p]==minc[scc]) nway[scc]++; top--; }while(st[top+1]!=x); } } void dfs(){ for(int i=1;i<=n;i++) if(!dfn[i])Tarjan(i); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&cost[i]); scanf("%d",&m); init(); for(int i=1;i<=m;i++){ scanf("%d%d",&a,&b); add(a,b); } dfs(); LL sum=0,sumn=1; for(int i=1;i<=scc;i++){ sum+=minc[i]; sumn=(sumn*nway[i])%mod; } printf("%lld %lld\n",sum,sumn); return 0; }