Luogu P2194 HXY烧情侣
省选前颓几道水题?
言归正传:
显然这是一道TARJAN,题面已经想方设法在提醒你了
只要记录每个强联通分量中的最小元素值和最小元素值对应的数量即可
看到很多题解都是全部处理完之后在最后统计最小元素值和最小元素对应数量并统计两个答案的,其实对于减小常数来说这样是没必要的,因为每个强连通分量只会在tarjan的途中被搜索到一次(可以叫做无后效性?)所以只要一边tarjan一边统计答案即可
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#define writep(x) write(x),putchar(' ')
#define writeln(x) write(x),puts("")
#define ha 1000000007//珍惜生命,暴力取膜不可取,所以,我们取ha
using namespace std;
inline int read(){
int ans=0,f=1;char chr=getchar();
while(!isdigit(chr)){if(chr=='-') f=-1;chr=getchar();}
while(isdigit(chr)){ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();}
return ans*f;
}void write(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}const int M=3e5+5;
int n,m,a[M],head[M],ver[M],nxt[M],tot,dfn[M],low[M],ins[M],sta[M],top,t,mn[M],col,color[M],cnt[M],mc[M],ans1,ans2=1;
/*
col 强连通分量数量
color[]强连通分量的编号
cnt[]强连通分量重元素个数(这里貌似没用)
mn[]强连通分量中值最小值
mc[]强连通分量中最小值个数
*/
inline void add(int x,int y){ver[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
inline void cmin(int &a,int b){if(a>b) a=b;}
void Tarjan(int x){
dfn[x]=low[x]=++t;ins[x]=1,sta[top++]=x;
for(register int i=head[x];i;i=nxt[i])
if(ins[ver[i]]==0) Tarjan(ver[i]),cmin(low[x],low[ver[i]]);
else if(ins[ver[i]]==1) cmin(low[x],dfn[ver[i]]);
if(dfn[x]==low[x]) ++col,mn[col]=0x7fffffff;
if(dfn[x]==low[x]){
do{
--top,++cnt[col],color[sta[top]]=col;ins[sta[top]]=-1;
if(mn[col]>a[sta[top]]) mn[col]=a[sta[top]],mc[col]=0;
if(mn[col]==a[sta[top]]) ++mc[col];//关键部分
}while(sta[top]!=x);
ans1+=mn[col];ans2=ans2*mc[col]%ha;//记录答案
}
}
int main(){
n=read();for(register int i=1;i<=n;++i) a[i]=read();
m=read();for(register int i=1,x,y;i<=m;++i){x=read(),y=read(),add(x,y);}
for(register int i=1;i<=n;++i) if(!dfn[i]) Tarjan(i);//基本操作
writep(ans1),writeln(ans2);//输出
return 0;
}