题目描述
众所周知,HXY已经加入了FFF团。现在她要开始喜(sang)闻(xin)乐(bing)见(kuang)地烧情侣了。这里有n座电影院,n对情侣分别在每座电影院里,然后电影院里都有汽油,但是要使用它需要一定的费用。m条单向通道连接相邻的两对情侣所在电影院。然后HXY有个绝技,如果她能从一个点开始烧,最后回到这个点,那么烧这条回路上的情侣的费用只需要该点的汽油费即可。并且每对情侣只需烧一遍,电影院可以重复去。然后她想花尽可能少的费用烧掉所有的情侣。问最少需要多少费用,并且当费用最少时的方案数是多少?由于方案数可能过大,所以请输出方案数对1e9+7取模的结果。
(注:这里HXY每次可以从任何一个点开始走回路。就是说一个回路走完了,下一个开始位置可以任选。所以说不存在烧不了所有情侣的情况,即使图不连通,HXY自行选择顶点进行烧情侣行动。且走过的道路可以重复走。)
输入输出格式
输入格式:
第一行,一个整数n。
第二行,n个整数,表示n个情侣所在点的汽油费。
第三行,一个整数m。
接下来m行,每行两个整数xi,yi,表示从点xi可以走到yi。
输出格式:
一行,两个整数,第一个数是最少费用,第二个数是最少费用时的方案数对1e9+7取模
输入输出样例
说明
数据范围:
对于30%的数据,1<=n,m<=20;
对于10%的数据,保证不存在回路。
对于100%的数据,1<=n<=100000,1<=m<=300000。所有输入数据保证不超过10^9。
很简单,tarjan拆点,对于方案数,用乘法计数原理不难解出。
AC代码如下:
#include<cstdio> #include<algorithm> #include<stack> using namespace std; const int M=300000+5; const int N=100000+5; const int INF=1e9+7; struct p{ int to,nxt; }e[M]; int fir[N],dfn[N],low[N],col[N],tot,num,t,x,y,n,m,mincost[N],cost[N],ans1,ans2=1,nu[N]; bool ins[N]; stack<int>s; void add(int u,int v) { tot++; e[tot].to=v; e[tot].nxt=fir[u]; fir[u]=tot; return; } void dfs(int now) { dfn[now]=low[now]=++t; s.push(now); ins[now]=1; for(int i=fir[now];i;i=e[i].nxt) if(!dfn[e[i].to]) dfs(e[i].to),low[now]=min(low[now],low[e[i].to]); else if(ins[e[i].to]) low[now]=min(low[now],dfn[e[i].to]); if(dfn[now]==low[now]) { num++; col[now]=num; mincost[num]=cost[now]; ins[now]=0; while(s.top()!=now) { col[s.top()]=num; mincost[num]=min(mincost[num],cost[s.top()]); ins[s.top()]=0; s.pop(); } s.pop(); } return; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&cost[i]); scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y); for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); for(int i=1;i<=num;i++) ans1+=mincost[i]; printf("%d ",ans1); for(int i=1;i<=n;i++) if(cost[i]==mincost[col[i]]) nu[col[i]]++; for(int i=1;i<=num;i++) ans2*=nu[i]%INF; printf("%d",ans2); return 0; }