您的位置:首页 > 其它

【洛谷P2194】 HXY烧情侣 强连通分量+tarjan+统计个数

2016-12-12 19:23 211 查看
题目描述

众所周知,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取模

输入输出样例

输入样例#1:

3

1 2 3

3

1 2

2 3

3 2

输出样例#1:

3 1

输入样例#2:

3

10 20 10

4

1 2

1 3

3 1

2 1

输出样例#2:

10 2

说明

数据范围:

对于30%的数据,1<=n,m<=20;

对于10%的数据,保证不存在回路。

对于100%的数据,1<=n<=100000,1<=m<=300000。所有输入数据保证不超过10^9。

题解:tarjan求强连通分量,求强连通分量的时候同时维护一个数组表示该强连通分量中花费最少的点的花费是多少。最后统计每个强连通分量等于该强连通分量最小花费的点有多少个,更新答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 300006
#define MOD 1000000007
using namespace std;
struct node
{
int from,to,next;
}e
;
int low
,dfn
,insex,stack
,tot;
bool instack
;
int t,ans
;
int e_num,head
;
bool flag
;
int r,n,p,num
,color
,ru
,m;
int a
,b
,s
;
void add(int from,int to)
{
++e_num;
e[e_num].from=from;
e[e_num].to=to;
e[e_num].next=head[from];
head[from]=e_num;
}
void tarjan(int u)
{
int i;
low[u]=dfn[u]=++insex;
instack[u]=true;
stack[++tot]=u;
for (i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if (!dfn[v])
{
tarjan(v);
low[u]=min(low[v],low[u]);
}
else if (instack[v])
{
low[u]=min(low[u],dfn[v]);
}
}
if (low[u]==dfn[u])
{
int tmp=0;
++t;
while (tmp!=u)
{
tmp=stack[tot--];
instack[tmp]=false;
ans[t]=min(ans[t],s[tmp]);
color[tmp]=t;
}
}
}
void slove()
{
int i;
memset(dfn,0,sizeof(dfn));
for (i=1;i<=n;i++)
if (!dfn[i])
tarjan(i);
}
int main()
{
int x,y,i,sum=0;
scanf("%d",&n);
memset(s,127/3,sizeof(s));
for (i=1;i<=n;i++)
{
scanf("%d",&x);
s[i]=x;
}
scanf("%d",&m);
for (i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
a[i]=x;b[i]=y; //x指向y
}
memset(ans,127/3,sizeof(ans));
int maxx=ans[0];
slove();
int tt=t;
for (i=1;i<=n;i++)
{
if (s[i]==ans[color[i]]) num[color[i]]++;
}
long long sum1=1;
for (i=1;i<=t;i++)
{
sum+=ans[i];
sum1*=num[i];
sum1=sum1%MOD;
}

printf("%d %lld",sum,sum1);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: