【HDU3639】Hawk and Chicken【tarjan】

6 篇文章 0 订阅

在这里插入图片描述
诸位先练习一下阅读理解。

在这里插入图片描述

分析

一开始我想到的是缩点之后用拓扑去递推就行,一直以为是对的但是调不出来,然后被旁边大佬一组数据hack掉了(非常崩溃崩溃bkbkbkbk找jq呜呜呜)。然后发现拓扑只要对于有“多对一”这样图的都无法计算或者去重,于是放弃2.5h的思路。

在思考拓扑的时候想过记录前驱,那不就变成DFS了吗?于是就打DFS。

DFS之前要反向建边,从入度(反图中)为0的节点开始DFS,因为只有这些点可以作为答案,然后记录每个强连通分量的最终答案(可见答案就是DFS中经过的点数总和),最后让每个点对应上强连通分量就可以了。

官方解释:
在这里插入图片描述

其实实现的时候非常多细节,比如两个邻接表的转换,一堆初始化,对于点的下标的+1处理,数组开大点等等。

上代码

又是类似调“疫情控制”的一天。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
using namespace std;

struct jq
{
	int to,next;
}e[50010],en[50010];

int t,n,m,id;
int hd[50010],tot;
int hdn[50010],totn;
int sum[5010],in[5010],mx,ans[5010],calc,vis[5010];
int dfn[5010],col[5010],low[5010],tmp[5010],timi,cnt;

void add(int x,int y)
{
	e[++tot]=(jq){y,hd[x]};
	hd[x]=tot;
}
void addn(int x,int y)
{
	en[++totn]=(jq){y,hdn[x]};
	hdn[x]=totn;
}

stack<int> st;
void tarjan(int x)
{
	dfn[x]=low[x]=++timi;
	st.push(x);
	for(int i=hd[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(!dfn[v])
		{
			tarjan(v);
			low[x]=min(low[x],low[v]);
		}
		else if(!col[v]) low[x]=min(low[x],low[v]);
	}
	if(dfn[x]==low[x])
	{
		col[x]=++cnt;
		sum[cnt]++; 
		while(st.top()!=x) 
		{
			sum[cnt]++;
			col[st.top()]=cnt;
			st.pop();
		}
		st.pop();
	}
}

void dfs(int x)
{
	vis[x]=1;
	calc+=sum[x];
	for(int i=hdn[x];i;i=en[i].next)
	{
		if(vis[en[i].to]) continue;
		dfs(en[i].to);
	}
}

int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n>>m;
		for(int i=1;i<=m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			add(x+1,y+1);
		}
		for(int i=1;i<=n;i++)
		{
			if(!dfn[i]) tarjan(i);
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=hd[i];j;j=e[j].next)
			{
				int v=e[j].to;
				if(col[i]!=col[v])
				{
					addn(col[v],col[i]);
					in[col[i]]++;
				}
			}
		}
		for(int i=1;i<=cnt;i++)
		{
			if(!in[i])
			{
				memset(vis,0,sizeof(vis));
				calc=0;
				dfs(i);
				ans[i]=calc-1;
				mx=max(mx,ans[i]);
			}
		}
		printf("Case %d: %d\n",++id,mx);
		for(int i=1;i<=n;i++)
		{
			if(ans[col[i]]==mx) cout<<i-1<<' ';
		}
		cout<<endl;
		memset(dfn,0,sizeof(dfn));
		memset(col,0,sizeof(col));
		memset(sum,0,sizeof(sum));
		memset(low,0,sizeof(low));
		tot=totn=cnt=mx=timi=0;
		memset(hd,0,sizeof(hd));
		memset(hdn,0,sizeof(hdn));
		memset(in,0,sizeof(in));
		memset(e,0,sizeof(e));
		memset(en,0,sizeof(en));
		memset(ans,0,sizeof(ans));
		while(!st.empty()) st.pop();
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值