强连通分量缩点应用:间谍网络

传送门luoguP1262
通过本题我们不难发现:
1.对于每一个点,那么入度为0必须贿赂,就可以去揭发其他的了
2.对于每一个环,如果一个环上存在一个可以行贿的间谍,那么这个环都可以解决,我们只需要找到每个环上的需要钱的最小值,把它看做一个点。点权值就是最小的钱。这样我们就只需通过找入度为0的点了

首先判断是否有点没有更新,即其他人无法揭露它,它也不能被检举,那么就输出NO
然后我们枚举每一个边所对应的2个点,如果两个点不属于同一连通分量,那么将指向的那个连通分量的入度加+1,最后找到入度为0的点,统计他们要的钱。
代码实现:

#include<bits/stdc++.h>
using namespace std;

#define num ch-'0'
void get(int &res)//快读 
{
    
    
    char ch;bool flag=0;
    while(!isdigit(ch=getchar()))
        (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getchar());res=res*10+num);
    (flag)&&(res=-res);
}

const int N=1e5+5,M=2e5+5;//一定要开大一点
int p,n,m,low[N],dfn[N],sum,id[N],u[N],v[N],r[N],ans,cnt,mon[N],t,w[N];
bool vis[N];
stack<int>a;
int first[N],nex[M],to[M],tot;

void add(int x,int y)
{
    
    
	nex[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}

void dfs(int x)//标准的tarjan 
{
    
    
	dfn[x]=low[x]=++cnt;
	a.push(x);
	vis[x]=1;
	
	for(int i=first[x];i;i=nex[i])
	{
    
    
		int y=to[i];
		
		if(dfn[y]==0)
		{
    
    
			dfs(y);
			low[x]=min(low[x],low[y]);
		}
		else
		{
    
    
			if(vis[y])
			{
    
    
				low[x]=min(low[x],dfn[y]);
			}
 		}
	}
	if(dfn[x]==low[x])
	{
    
    
		sum++;int t;
		do
		{
    
    
			t=a.top();
			vis[t]=0;
			id[t]=sum;
			w[id[t]]=min(w[id[t]],mon[t]);//这里加一个,统计每个强连通的最小钱 
			a.pop();
		}while(t!=x);
	}
}



int main()
{
    
    
	get(n);get(p);
	
	memset(mon,0x3f,sizeof(mon));//表示每个需要的钱,并初始化 
	memset(w,0x3f,sizeof(w));//表示每个强连通最小需要的钱,并初始化 
	for(int i=1;i<=p;i++)
	{
    
    
		int num1;
		get(num1);
		get(mon[num1]);
	}
	get(m);
	for(int i=1;i<=m;i++)
	{
    
    
		int x,y;
		get(x);get(y);
		u[i]=x;v[i]=y;//记录一下每条边上对应的2个点 
		add(x,y);
	}
	
	for(int i=1;i<=n;i++)
	{
    
    
		if(dfn[i]==0 && mon[i]!=0x3f3f3f3f) dfs(i); //可以被行贿且没有被更新 
	}

	for(int i=1;i<=n;i++)
	{
    
    
		if(dfn[i]==0)//更新不到,且不能贿赂 
		{
    
    
			cout<<"NO"<<endl<<i;
			return 0;
		}
	}
	//现在我们就把id看做每一个点,缩点。 
	for(int i=1;i<=m;i++)
	{
    
    
		if(id[u[i]]!=id[v[i]])//如果两点不属于一个连通分量 
		{
    
    
			r[id[v[i]]]++;//入度加1 
		}
	}
	for(int i=1;i<=sum;i++)
	{
    
    
		if(r[i]==0) ans+=w[i];//入度为0的点必须贿赂 
	}
	cout<<"YES"<<endl;
	cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/pigonered/article/details/120938749
今日推荐