并查集

目录

一,并查集

1,模板

2,按秩合并

3,时间复杂度

二,带权并查集

三,集合并查集

四,OJ实战

1,路径压缩查找

POJ 1308 Is It A Tree?

POJ 1611 The Suspects

HDU 1558 Segment set

POJ 2524 Ubiquitous Religions

HDU 3172 Virtual Friends

2,带权并查集

CSU 1045 并查集(集合元素个数)

CSU 1180 盒子里的球(差分)

POJ 1182 食物链(差分)

CodeForces 115A Party(深度)

3,力扣实战


一,并查集

并查集是一种森林,而且是反向森林,即每个节点都指向自己的父亲节点。

根据森林中根节点的个数,所有的节点都分成了若干个不相交的集合。

1,模板

并查集的主要操作:查找一个节点的祖先、合并两颗树

class Union
{
public:
	Union(int num, bool canZip = true)
	{
		fa.resize(num);
		for (int i = 0; i < fa.size(); i++)fa[i] = i;
		this->canZip = canZip;
	}
	int find(int x)	//找祖先,canZip控制能否做路径压缩加速
	{
		if (canZip) {
			if (fa[x] == x)return x;
			return fa[x] = find(fa[x]);
		}
		int r = x;
		while (fa[r] != r)r = fa[r];
		return r;
	}
	bool inSame(int x, int y)//是否位于同一个集合
	{
		return find(x) == find(y);
	}
	void merge(int x, int y)//合并2个集合,如果是同一个集合则不做操作
	{
		if (!inSame(x, y))fa[find(x)] = y;
	}
	vector<int> getRoots()//获取所有根节点
	{
		vector<int>ans;
		ans.reserve(fa.size());
		for (int i = 0; i < fa.size(); i++)if (fa[i] == i)ans.push_back(i);
		return ans;
	}
	int getRootNums()//统计根节点数目
	{
		return getRoots().size();
	}
	vector<vector<int>> getGroups()
	{
		vector<int> roots = getRoots();
		map<int, int>m = reflect(roots);
		vector<vector<int>>ans(m.size());
		for (int i = 0; i < fa.size(); i++)ans[m[find(i)]].push_back(i);
		return ans;
	}
private:
	vector<int>fa;
	bool canZip;
	template<typename T>
	map<T, int> reflect(const vector<T>& v)
	{
		map<T, int>m;
		for (int i = 0; i < v.size(); i++)m[v[i]] = i;
		return m;
	}
};

2,按秩合并

一棵树的深度就是秩,按秩合并就是在合并两棵树时,选择秩更大的树的根作为新的根,另外一个作为其子节点。

3,时间复杂度

这里的UNION是按秩合并,α函数是一个增长极快的函数的逆函数。

这个增长极快的函数具体是什么函数我没看出来,不过有点类似阿克曼函数

二,带权并查集

常见的带权并查集:

(1)计算每个集合有多少个数,这个用上面的路径压缩查找即可实现。

(2)计算每个节点的深度,这种只能用上面的普通查找,不能用路径压缩。

(3)根据节点和父节点的差分,计算所有节点的权值,这种可以用路径压缩查找,模板如下:

class UnionDif //差分版并查集,依赖路径压缩
{
public:
	UnionDif(int num)
	{
		fa.resize(num);
		for (int i = 0; i < fa.size(); i++)fa[i] = i;
	}
	int find(int x)	//找祖先
	{
		if (fa[x] == x)return x;
		find(fa[x]);
		dif[x] += dif[fa[x]];
		fa[x] = fa[fa[x]];
		return fa[x];
	}
	bool inSame(int x, int y)//是否位于同一个集合
	{
		return find(x) == find(y);
	}
	void merge(int x, int y, double xSubY = 0)//合并2个集合,如果是同一个集合则不做操作
	{
		if (inSame(x, y))return;
		find(x);
		dif[fa[x]] = xSubY - dif[x];
		fa[fa[x]] = y;
		return;
	}
	double getDif(int x)
	{
		return dif[x];
	}
	vector<int> getRoots()//获取所有根节点
	{
		vector<int>ans;
		ans.reserve(fa.size());
		for (int i = 0; i < fa.size(); i++)if (fa[i] == i)ans.push_back(i);
		return ans;
	}
	int getRootNums()//统计根节点数目
	{
		return getRoots().size();
	}
	vector<vector<int>> getGroups()
	{
		vector<int> roots = getRoots();
		map<int, int>m = reflect(roots);
		vector<vector<int>>ans(m.size());
		for (int i = 0; i < fa.size(); i++)ans[m[find(i)]].push_back(i);
		return ans;
	}
private:
	vector<int>fa;
	map<int, double>dif;//每个节点和fa的差分
	template<typename T>
	map<T, int> reflect(const vector<T>& v)
	{
		map<T, int>m;
		for (int i = 0; i < v.size(); i++)m[v[i]] = i;
		return m;
	}
};

三,集合并查集

场景:一次性给出若干个集合,任意2个集合之间如果有共同元素则连一条边,最终得到一个无向图。

template<typename T>
class Vunion:public Union
{
public:
	Vunion(int num) :Union{ num } {};
	void push(vector<vector<T>>&v) {
		map<T, vector<int>>m;
		for (int i = 0; i < v.size(); i++)for (auto x : v[i])m[x].push_back(i);
		for (auto &p : m) {
			for (auto x : p.second)merge(x, p.second[0]);
		}
	}
};

四,OJ实战

1,路径压缩查找

POJ 1308 Is It A Tree?

Description

A tree is a well-known data structure that is either empty (null, void, nothing) or is a set of one or more nodes connected by directed edges between nodes satisfying the following properties. 

There is exactly one node, called the root, to which no directed edges point. 
Every node except the root has exactly one edge pointing to it. 
There is a unique sequence of directed edges from the root to each node. 
For example, consider the illustrations below, in which nodes are represented by circles and edges are represented by lines with arrowheads. The first two of these are trees, but the last is not. 


In this problem you will be given several descriptions of collections of nodes connected by directed edges. For each of these you are to determine if the collection satisfies the definition of a tree or not.
Input

The input will consist of a sequence of descriptions (test cases) followed by a pair of negative integers. Each test case will consist of a sequence of edge descriptions followed by a pair of zeroes Each edge description will consist of a pair of integers; the first integer identifies the node from which the edge begins, and the second integer identifies the node to which the edge is directed. Node numbers will always be greater than zero.
Output

For each test case display the line "Case k is a tree." or the line "Case k is not a tree.", where k corresponds to the test case number (they are sequentially numbered starting with 1).
Sample Input

6 8  5 3  5 2  6 4
5 6  0 0

8 1  7 3  6 2  8 9  7 5
7 4  7 8  7 6  0 0

3 8  6 8  6 4
5 3  5 6  5 2  0 0
-1 -1
Sample Output

Case 1 is a tree.
Case 2 is a tree.
Case 3 is not a tree.

这个题目,除了要满足是连通无环图之外,还必须有唯一的根。

每次输入m,n,m是n的父亲,对m不需要检测,只需要检测n。

n要么是第一次出现,要么出现过但是一直是自己的父亲,不能以其他点为父亲。

还有一个隐晦的地方需要注意,如果n满足自己是自己的父亲,还需要判断m是不是n的后代,否则会形成环。

代码:

#include<iostream>
#include<cstring>
using namespace std;

int fa[100001];
int num[100001];
int sum;

int find(int x)
{
	int r = x;
	while (fa[r] != r)r = fa[r];
	int f;
	while (fa[x] != x)
	{
		f = fa[x];
		fa[x] = r;
		x = f;
	}
	return r;
}


int main()
{
	int m, n, max1, max2, cas = 1;
	while (1)
	{
		bool flag = true;
		sum = 0, max1 = 0, max2 = 0;
		while (scanf("%d%d", &m, &n))
		{
			if (m == 0 && n == 0)break;
			if (m == -1 && n == -1)return 0;
			if (flag == false)continue;
			if (max2 < m) max2 = m;
			if (max2 < n) max2 = n;
			for (int i = max1 + 1; i <= max2; i++)fa[i] = -1;
			max1 = max2;
			if (fa[m] < 0)
			{
				fa[m] = m;
				num[m] = 1;
				sum++;
			}
			if (fa[n] >= 0)
			{
				if (fa[n] != n)flag = false;
				else if (find(m) == n)flag = false;
				else
				{
					num[find(m)] += num[n];
					fa[n] = m;
				}
			}
			else
			{
				fa[n] = m;
				num[find(m)]++;
				sum++;
			}
		}
		printf("Case %d", cas++);
		if (flag && num[find(max2)] == sum)printf(" is a tree.\n");
		else printf(" is not a tree.\n");
	}
	return 0;
}

有个很类似的题目:HDU 1272 小希的迷宫

那个题目,虽然和这个题目很像,但是还是有区别的。

有人拿那个题目的代码稍微一改就AC了,我用源代码做了测试,可以AC,

可是却连下面这个例子都算不对,只能说POJ这个题目的judge其实还不完善。

1 2

1 3

4 3

0 0

POJ 1611 The Suspects

题目:

Description

Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized as a global threat in mid-March 2003. To minimize transmission to others, the best strategy is to separate the suspects from others. 
In the Not-Spreading-Your-Sickness University (NSYSU), there are many student groups. Students in the same group intercommunicate with each other frequently, and a student may join several groups. To prevent the possible transmissions of SARS, the NSYSU collects the member lists of all student groups, and makes the following rule in their standard operation procedure (SOP). 
Once a member in a group is a suspect, all members in the group are suspects. 
However, they find that it is not easy to identify all the suspects when a student is recognized as a suspect. Your job is to write a program which finds all the suspects.
Input

The input file contains several cases. Each test case begins with two integers n and m in a line, where n is the number of students, and m is the number of groups. You may assume that 0 < n <= 30000 and 0 <= m <= 500. Every student is numbered by a unique integer between 0 and n−1, and initially student 0 is recognized as a suspect in all the cases. This line is followed by m member lists of the groups, one line per group. Each line begins with an integer k by itself representing the number of members in the group. Following the number of members, there are k integers representing the students in this group. All the integers in a line are separated by at least one space. 
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.
Output

For each case, output the number of suspects in one line.
Sample Input

100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0
Sample Output

4
1
1

代码:

#include<iostream>
#include<cstring>
using namespace std;
 
int fa[30005];
int num[30005];
 
int find(int x)		//找祖先
{
	int r = x;
	while (fa[r] != r)r = fa[r];
	int f;
	while (fa[x] != x)
	{
		f = fa[x];
		fa[x] = r;
		x = f;
	}
	return r;
}
 
int main()
{
	ios_base::sync_with_stdio(false);
	int n, m, nu, a, b, sum;
	while (cin >> n >> m)
	{
		if (n == 0)break;
		for (int i = 0; i <= n; i++)fa[i] = -1;
		while (m--)
		{
			cin >> nu;
			if (nu == 0)continue;
			nu--;			
			cin >> a;
			if (fa[a] < 0)
			{
				fa[a] = a;
				num[a] = 1;
			}
			int f = find(a);
			while (nu--)
			{
				cin >> b;
				if (fa[b] < 0)
				{
					fa[b] = f;
					num[f]++;
				}
				else if (find(b)!=f)
				{
					num[f] += num[find(b)];
					fa[find(b)] = f;
				}
			}
		}
		if (fa[0] < 0)sum=1;
		else sum = num[find(0)];
		cout << sum << endl;
	}
	return 0;
}

HDU 1558 Segment set

题目:

Description

A segment and all segments which are connected with it compose a segment set. The size of a segment set is the number of segments in it. The problem is to find the size of some segment set. 


Input

In the first line there is an integer t - the number of test case. For each test case in first line there is an integer n (n<=1000) - the number of commands. 

There are two different commands described in different format shown below: 

P x1 y1 x2 y2 - paint a segment whose coordinates of the two endpoints are (x1,y1),(x2,y2). 
Q k - query the size of the segment set which contains the k-th segment. 

k is between 1 and the number of segments in the moment. There is no segment in the plane at first, so the first command is always a P-command. 
Output

For each Q-command, output the answer. There is a blank line between test cases.
Sample Input

1
10
P 1.00 1.00 4.00 2.00
P 1.00 -2.00 8.00 4.00
Q 1
P 2.00 3.00 3.00 1.00
Q 1
Q 3
P 1.00 4.00 8.00 2.00
Q 2
P 3.00 3.00 6.00 -2.00
Q 5

Sample Output

1
2
2
2
5

这个题目还真是人性化啊,上面那个图,满分!

首先写了个函数判断2个线段是否相交,最后运行的时候才发现,我写的只是判断2个点是否在一条直线的2边而已。

然后上网查了一下才发现,好傻,只要a的端点在b的2边,b的端点也在a的2边,那不就是相交了吗。

代码:

#include<iostream>
using namespace std;
 
struct seg
{
	int fa;
	double x1, y1, x2, y2;
};
 
seg s[1001];
 
bool con(seg a, seg b)		//b的2个端点是否在a的2侧
{
	if ((a.x2 - a.x1)*(b.y1 - a.y1) == (a.y2 - a.y1)*(b.x1 - a.x1))return true;
	if ((a.x2 - a.x1)*(b.y2 - a.y1) == (a.y2 - a.y1)*(b.x2 - a.x1))return true;
	int t = ((a.x2 - a.x1)*(b.y1 - a.y1) > (a.y2 - a.y1)*(b.x1 - a.x1));
	t += ((a.x2 - a.x1)*(b.y2 - a.y1) > (a.y2 - a.y1)*(b.x2 - a.x1));
	return (t == 1);
}
 
int find(int x)		//找第i条线段的祖先
{
	int r = x;
	while (s[r].fa != r)r = s[r].fa;
	while (s[x].fa != x)
	{
		int  f = s[x].fa;
		s[x].fa = r;
		x = f;
	}
	return r;
}
 
 
int main()
{
	int t, n, q, faq, sum;
	char c;
	cin >> t;
	while (t--)
	{
		cin >> n;
		int i = 1;
		while (n--)
		{
			cin >> c;
			if (c == 'P')
			{
				cin >> s[i].x1 >> s[i].y1 >> s[i].x2 >> s[i].y2;
				s[i].fa = i;
				for (int j = 1; j < i; j++)
		if (con(s[j], s[i]) && con(s[i], s[j]))s[find(j)].fa = i;
				i++;
			}
			else
			{
				cin >> q;
				faq = find(q);
				sum = 0;
				for (int j = 1; j < i; j++)sum += (find(j) == faq);
				cout << sum << endl;
			}
		}
		if(t)cout << endl;
	}
	return 0;
}

POJ 2524 Ubiquitous Religions

题目:

There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in finding out how many different religions students in your university believe in. 

You know that there are n students in your university (0 < n <= 50000). It is infeasible for you to ask every student their religious beliefs. Furthermore, many students are not comfortable expressing their beliefs. One way to avoid these problems is to ask m (0 <= m <= n(n-1)/2) pairs of students and ask them whether they believe in the same religion (e.g. they may know if they both attend the same church). From this data, you may not know what each person believes in, but you can get an idea of the upper bound of how many different religions can be possibly represented on campus. You may assume that each student subscribes to at most one religion.
Input
The input consists of a number of cases. Each case starts with a line specifying the integers n and m. The next m lines each consists of two integers i and j, specifying that students i and j believe in the same religion. The students are numbered 1 to n. The end of input is specified by a line in which n = m = 0.
Output
For each test case, print on a single line the case number (starting with 1) followed by the maximum number of different religions that the students in the university believe in.
Sample Input
10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0
Sample Output
Case 1: 1
Case 2: 7
Hint
Huge input, scanf is recommended.


题意:

输入n,m,有n个人,m个二人组

每个二人组表示2个人信仰相同

最后求一共最多有多少种信仰

代码:

#include<iostream>
#include<stdio.h>
using namespace std;
 
int fa[500005];
 
int find(int x)	//找祖先
{
	if (fa[x] == x)return x;
	return fa[x] = find(fa[x]);
}
 
int main()
{
	int n, m, ca = 0, x, y;
	while (scanf("%d%d",&n,&m))
	{
		if (n == 0)break;
		for (int i = 1; i <= n; i++)fa[i] = i;
		while (m--)
		{
			scanf("%d%d", &x, &y);
			if (find(x) != find(y))fa[fa[x]] = fa[y];
		}
		int ans = 0;
		for (int i = 1; i <= n; i++)if (fa[i] == i)ans++;
		printf("Case %d: %d\n", ++ca, ans);
	}
	return 0;
}

HDU 3172 Virtual Friends

题目:


Description

These days, you can do all sorts of things online. For example, you can use various websites to make virtual friends. For some people, growing their social network (their friends, their friends' friends, their friends' friends' friends, and so on), has become an addictive hobby. Just as some people collect stamps, other people collect virtual friends. 

Your task is to observe the interactions on such a website and keep track of the size of each person's network. 

Assume that every friendship is mutual. If Fred is Barney's friend, then Barney is also Fred's friend.
Input

Input file contains multiple test cases. 
The first line of each case indicates the number of test friendship nest. 
each friendship nest begins with a line containing an integer F, the number of friendships formed in this frindship nest, which is no more than 100 000. Each of the following F lines contains the names of two people who have just become friends, separated by a space. A name is a string of 1 to 20 letters (uppercase or lowercase).
Output

Whenever a friendship is formed, print a line containing one integer, the number of people in the social network of the two people who have just become friends.
Sample Input

1
3
Fred Barney
Barney Betty
Betty Wilma
Sample Output

2
3
4

除了1个数组记录father,还需要1个数组记录这个集合中的元素数目。

num数组并不是每个数都有意义,只有每个集合的根的num才是这个集合的数目,其他绝大部分num都再也没用了。

(貌似数组开小了,应该扩大1倍,不过还是AC了)

代码:
 

#include<iostream>
#include<map>
using namespace std;
 
map<string, int>m;
int fa[100005];
int num[100005];
char s[21];
int k1, k2;
int t, f, k;
map<string, int>::iterator p;
 
int find(int x)		//找祖先
{
	int r = x;
	while (fa[r] != r)r = fa[r];
	while (fa[x] != x)
	{
		int  f = fa[x];
		fa[x] = r;
		x = f;
	}
	return r;
}
 
int mapfind(string s)
{
	p = m.find(s);
	if (p == m.end())
	{
		m.insert(pair<string, int>(s, k));
		fa[k] = k;
		num[k++] = 1;
	}
	return find(m.find(s)->second);
}
 
int main()
{
	while (cin >> t)
	{
		while (t--)
		{
			cin >> f;
			k = 1;
			m.clear();
			while (f--)
			{
				cin >> s;
				k1 = mapfind(s);
				cin >> s;
				k2 = mapfind(s);
				if (k1 != k2)
				{
					fa[k1] = k2;
					num[k2] += num[k1];
				}
				cout << num[k2] << endl;
			}
		}
	}
	return 0;
}

2,带权并查集

CSU 1045 并查集(集合元素个数)

题目:

Description
大一的学一下,大二以上还不会并查集的统统去面壁。

Input
多组数据,每组第一行两个正整数n,m,表示有1~n这n个编号,m个关系。

接下来m行,每行两个数i, j, 1 <= i, j <= n,表示i和j是一组的。

每个编号自己和自己是一组的。

1 < n < 1000000 ,1 < m < 100000 。

Output
每组数据输出一行,一个数,表示组员最多的组的组员个数。

Sample Input
10 5
1 2
3 5
2 6
4 7
9 6
Sample Output
4


代码:
 

#include<iostream>
using namespace std;
 
int fa[1000005], num[1000005];
 
int find(int x)		//找祖先
{
	int r = x;
	while (fa[r] != r)r = fa[r];
	int f;
	while (fa[x] != x)
	{
		f = fa[x];
		fa[x] = r;
		x = f;
	}
	return r;
}
 
int main()
{
	int n, m, a, b;
	while (cin >> n >> m)
	{
		for (int i = 0; i <= n; i++)fa[i] = -1;
		while (m--)
		{
			cin >> a;
			if (fa[a] < 0)
			{
				fa[a] = a;
				num[a] = 1;
			}
			int f = find(a);
			cin >> b;
			if (fa[b] < 0)
			{
				fa[b] = f;
				num[f]++;
			}
			else if (find(b) != f)
			{
				num[f] += num[find(b)];
				fa[find(b)] = f;
			}
		}
		int ans = 0;
		for (int i = 1; i <= n; i++)
			if (fa[i] > 0 && ans < num[find(i)])ans = num[find(i)];
		cout << ans << endl;
	}
	return 0;
}

CSU 1180 盒子里的球(差分)

题目:

Description
 有n个盒子,m个判断,每个判断的内容是第s个盒子到第e个盒子的球的总数除以3的余数,求最大的k,使得存在一种情况,满足前k个判断。

Input
 多组数据,每组第一行两个数n和m,标识盒子个数和判断个数,接下来m行每行三个数s,e,s_e,表示第s个盒子和第e个盒子之间(包括s和e)所有盒子的球总数除以3的余数为s_e。

2 <= n <= 100000,0 <= m <= 100000,1 <= s,e <= n。
Output
 每组数据对应一行输出,为题目要求的K。

Sample Input
5 3
1 3 2
1 2 1
2 3 2
5 4
1 3 0
1 2 1
2 3 0
2 3 1
Sample Output
3
3


思路:

显然是并查集,用dfa数组存下每个数和它的父亲的差分即可

代码:
 

#include<iostream>
using namespace std;
 
int fa[100005], dfa[100005];
 
int find(int x)		//找祖先
{
	if (fa[x] == x)return x;
	int f = fa[x];
	fa[x] = find(f), dfa[x] += dfa[f];
	return fa[x];
}
 
int main()
{
	int n, m, a, b, c, ans;
	while (cin >> n >> m)
	{
		for (int i = 0; i <= n; i++)fa[i] = i, dfa[i] = 0;
		ans = m;
		for (int i = 0; i < m; i++)
		{
			cin >> a >> b >> c;
			if (a > b)a ^= b ^= a ^= b;
			int fia = find(a-1), fib = find(b);
			if (fia != fib)fa[fia] = fib, dfa[fia] = dfa[b] - dfa[a - 1] - c;
			else if((dfa[b] - dfa[a - 1] - c) % 3 && ans == m)ans = i;
		}
		cout << ans << endl;
	}
	return 0;
}

POJ 1182 食物链(差分)

题目:

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。 
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述: 
第一种说法是"1 X Y",表示X和Y是同类。 
第二种说法是"2 X Y",表示X吃Y。 
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 
1) 当前的话与前面的某些真的话冲突,就是假话; 
2) 当前的话中X或Y比N大,就是假话; 
3) 当前的话表示X吃X,就是假话。 
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。 
Input
第一行是两个整数N和K,以一个空格分隔。 
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 
若D=1,则表示X和Y是同类。 
若D=2,则表示X吃Y。
Output
只有一个整数,表示假话的数目。
Sample Input
100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5
Sample Output
3

这个题目其实和CSU 1180: 盒子里的球差不多是一样的

代码:

#include<iostream>
#include<stdio.h>
using namespace std;
 
int fa[50005], dfa[50005];
 
int find(int x)		//找祖先
{
	if (fa[x] == x)return x;
	int f = fa[x];
	fa[x] = find(f), dfa[x] += dfa[f];
	return fa[x];
}
 
int main()
{
	int n, m, d, x, y, ans = 0;
	scanf("%d%d", &n, &m);
	for (int i = 0; i <= n; i++)fa[i] = i, dfa[i] = 0;
	while (m--)
	{
		scanf("%d%d%d", &d, &x, &y);
		if (x > n || y > n)
		{
			ans++;
			continue;
		}
		int fx = find(x), fy = find(y);
		if (fx != fy)fa[fx] = fy, dfa[fx] = dfa[y] - dfa[x] + d - 1;
		else if (((dfa[x] - dfa[y]) % 3 + 3) % 3 != d - 1)ans++;
	}
	cout << ans << endl;
	return 0;
}

CodeForces 115A Party(深度)

题目:


Description

A company has n employees numbered from 1 to n. Each employee either has no immediate manager or exactly one immediate manager, who is another employee with a different number. An employee A is said to be the superior of another employee B if at least one of the following is true:

Employee A is the immediate manager of employee B
Employee B has an immediate manager employee C such that employee A is the superior of employee C.
The company will not have a managerial cycle. That is, there will not exist an employee who is the superior of his/her own immediate manager.

Today the company is going to arrange a party. This involves dividing all n employees into several groups: every employee must belong to exactly one group. Furthermore, within any single group, there must not be two employees Aand B such that A is the superior of B.

What is the minimum number of groups that must be formed?

Input

The first line contains integer n (1 ≤ n ≤ 2000) — the number of employees.

The next n lines contain the integers pi (1 ≤ pi ≤ n or pi = -1). Every pi denotes the immediate manager for the i-th employee. If pi is -1, that means that the i-th employee does not have an immediate manager.

It is guaranteed, that no employee will be the immediate manager of him/herself (pi ≠ i). Also, there will be no managerial cycles.

Output

Print a single integer denoting the minimum number of groups that will be formed in the party.

Sample Input

Input
5
-1
1
2
1
-1
Output
3

这个题目就是给你一个有向无环图,要你求最长路径。

可以用搜索的方法,也可以用并查集。

并查集的方法是,把这个图看成若干颗树,求这些树的深度的最大值。

只需要记录每个节点的father即可得到所有的树。

比如本题:

有2颗树,深度分别为3和1,所以答案是3

代码:

#include<iostream>
#include<stdio.h>
using namespace std;
 
int fa[2001];
int deep[2001];
 
int getdeep(int i)
{
	if (deep[i]>0)return deep[i];
	if (fa[i] == -1)return 1;
	deep[i] = getdeep(fa[i]) + 1;
	return deep[i];
}
 
int main()
{
	int n, r = 0;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> fa[i];
		deep[i] = -1;
	}
	for (int i = 1; i <= n; i++)if (r < getdeep(i))r = getdeep(i);
	cout << r;
	return 0;
}

3,力扣实战

力扣

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值