题解 CF427C 【Checkposts】
\[\text{关于题意}
\]
\(\quad\)因为点i能保护j的条件是i可以到达j且j可以返回i,这不就是强连通分量的定义吗?所以考虑缩点,将每个强连通分量缩成一个点,缩点后每个强连通分量中只需取一个点,所以记录每个强连通分量中最小点权及其数量,最后最低成本就是每个强连通分量最小点权之和,方案数是最小点权数量之积。
\(\quad\)别忘了开long long,最后方案数要对1e9+7取模。
\(\quad\)如果还有什么不懂的地方就看看代码吧
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<map>
#include<stack>
#include<algorithm>
#include<vector>
#define re register int
#define int long long
#define il inline
#define next ne
using namespace std;
il int read() //快速读入
{
int x=0,minus=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')minus=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*minus;
}
const int N=1e5+5,MOD=1e9+7;
int n,m,a[N],next[N*3],go[N*3],head[N];
int low[N],dfn[N],c[N],s[N],p[N],l[N],tot,cnt,ans,ans1=1;
il void Add(int x,int y) //链式前向星存图
{
next[++tot]=head[x];
head[x]=tot;
go[tot]=y;
}
stack<int>q;
il void Tarjan(int x) //Tarjan缩点
{
dfn[x]=low[x]=++cnt;
q.push(x);
for(re i=head[x];i;i=next[i])
{
int y=go[i];
if(!dfn[y])Tarjan(y),low[x]=min(low[x],low[y]);
else if(!c[y])low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
c[x]=++c[0];
s[c[0]]=a[x];p[c[0]]=1; //记录每个强连通分量中点权最小的,数量初始化为1
while(q.top()!=x)
{
c[q.top()]=c[0];
if(s[c[0]]==a[q.top()])p[c[0]]++; //如果有一样小的点权,数量+1
else if(s[c[0]]>a[q.top()])s[c[0]]=a[q.top()],p[c[0]]=1; //如果有更小的点权就更新,数量变为1
q.pop();
}
q.pop();
}
}
signed main()
{
n=read();
for(re i=1;i<=n;i++)a[i]=read();
m=read();
for(re i=1;i<=m;i++)
{
int x=read(),y=read();
Add(x,y);
}
for(re i=1;i<=n;i++)
if(!dfn[i])Tarjan(i);
for(re i=1;i<=c[0];i++)
{
ans+=s[i]; //最小点权之和
ans1*=p[i]%MOD; //数量之积
}
printf("%lld %lld",ans,ans1%MOD);
return 0;
}
\[\text{后话}
\]
此题有双倍经验哦,P2194 HXY烧情侣
临近CSP复赛,祝大家RP++。
写题解不易,帮忙点个赞吧。