题解 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++。

写题解不易,帮忙点个赞吧。

posted @ 2020-11-02 21:25  Farkas_W  阅读(110)  评论(0编辑  收藏  举报