LOJ #10091. 「一本通 3.5 例 1」受欢迎的牛

https://loj.ac/problem/10091

题目描述

原题来自:USACO 2003 Fall

每一头牛的愿望就是变成一头最受欢迎的牛。现在有 NNN 头牛,给你 MMM 对整数 (A,B)(A,B)(A,B),表示牛 AAA 认为牛 BBB 受欢迎。这种关系是具有传递性的,如果 AAA 认为 BBB 受欢迎,BBB 认为 CCC 受欢迎,那么牛 AAA 也认为牛 CCC 受欢迎。你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。

输入格式

第一行两个数 N,MN,MN,M;
接下来 MMM 行,每行两个数 A,BA,BA,B,意思是 AAA 认为 BBB 是受欢迎的(给出的信息有可能重复,即有可能出现多个 A,BA,BA,B)。

输出格式

输出被除自己之外的所有牛认为是受欢迎的牛的数量。

样例

样例输入

3 3
1 2
2 1
2 3

样例输出

1

样例说明

只有第三头牛被除自己之外的所有牛认为是受欢迎的。

数据范围与提示

对于全部数据,1≤N≤104,1≤M≤5×1041\le N\le 10^4,1\le M\le 5\times 10^41≤N≤104,1≤M≤5×104。

 

当这是一棵树的时候,很容易看出求每个点的出度,输出是否存在出度为0的点

(如果有多个结点,是不可能有奶牛被其余牛都欢迎,答案为0)

但这是一个有向图,所以要缩点后进行如上的操作

#include<cstdio>
#include<iostream>
using namespace std;
int read()
{
	int ret=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9')
		ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
	return ret;
}

const int N=1e6+4;
int n,m,a[N],d[N],ans;
int he[N],to[N],nxt[N],cnt;
int dfn[N],sgn,low[N],st[N],top,co[N],col;
struct NA{
	int u,v;
}e[N];

inline void add(int u,int v)
{
	to[++cnt]=v;
	nxt[cnt]=he[u];
	he[u]=cnt;
}

void Tarjan(int u)
{
	dfn[u]=low[u]=++sgn;
	st[++top]=u;
	for(int e=he[u];e;e=nxt[e])
	{
		int v=to[e];
		if(!dfn[v]) 
			Tarjan(v),low[u]=min(low[u],low[v]);
			else if(!co[v]) 
				low[u]=min(low[u],dfn[v]); 	
	}	
	if(dfn[u]==low[u])
	{
		co[u]=++col;
		while(top&&st[top]!=u)
			co[st[top--]]=col;
		top--;
	}
}

int main()
{
	n=read(),m=read();
	for(int i=1;i<=m;i++)
		e[i].u=read(),e[i].v=read(),
		add(e[i].u,e[i].v);
	for(int i=1;i<=n;i++)
		if(!dfn[i]) Tarjan(i);
	for(int i=1;i<=n;i++)
		a[co[i]]++;
	for(int i=1;i<=m;i++)
		if(co[e[i].v]!=co[e[i].u])
			d[co[e[i].u]]++;
	for(int i=1;i<=col;i++)
		if(!d[i])
		{
			if(!ans) ans=a[i];
				else 
				{
					ans=0; break;
				}
		}
	printf("%d\n",ans);
}

 

 

可并堆是一种支持合并操作的堆数据结构,常见的可并堆有左偏树、斜堆、二项堆等。对于 LOJ#P188 可并堆的问题,下面以左偏树为给出解题思路和代码实现。 ### 解题思路 1. **左偏树的性质**: - 左偏树是一种可并堆,它满足堆性质(小根堆或大根堆),即每个节点的值小于(或大于)其子节点的值。 - 左偏树还满足左偏性质,即每个节点的左子树的距离(到最近的叶子节点的距离)不小于右子树的距离。 2. **合并操作**: - 合并两个左偏树时,比较两个根节点的值,将值较大的根节点的树合并到值较小的根节点的右子树中。 - 合并后,检查右子树的距离是否大于左子树的距离,如果是,则交换左右子树,以维护左偏性质。 3. **插入操作**: - 插入一个新节点可以看作是合并一个只有一个节点的左偏树和原左偏树。 4. **删除操作**: - 删除根节点后,将其左右子树合并成一个新的左偏树。 ### 代码实现 ```python class Node: def __init__(self, val): self.val = val self.left = None self.right = None self.dist = 0 def merge(x, y): if not x: return y if not y: return x if x.val > y.val: x, y = y, x x.right = merge(x.right, y) if not x.left or (x.right and x.left.dist < x.right.dist): x.left, x.right = x.right, x.left x.dist = (x.right.dist + 1) if x.right else 0 return x def insert(root, val): new_node = Node(val) return merge(root, new_node) def delete(root): return merge(root.left, root.right) #使用 root = None root = insert(root, 3) root = insert(root, 1) root = insert(root, 5) print(root.val) # 输出堆顶元素 root = delete(root) print(root.val) # 输出删除堆顶元素后的堆顶元素 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值