Codeforces Round #692 (Div. 2) C. Peaceful Rooks(图论,并查集)

题目描述

You are given a n×n chessboard. Rows and columns of the board are numbered from 1 to n. Cell (x,y) lies on the intersection of column number x and row number y.
Rook is a chess piece, that can in one turn move any number of cells vertically or horizontally. There are m rooks (m<n) placed on the chessboard in such a way that no pair of rooks attack each other. I.e. there are no pair of rooks that share a row or a column.
In one turn you can move one of the rooks any number of cells vertically or horizontally. Additionally, it shouldn’t be attacked by any other rook after movement. What is the minimum number of moves required to place all the rooks on the main diagonal?
The main diagonal of the chessboard is all the cells (i,i), where 1≤i≤n.

Input

The first line contains the number of test cases t (1≤t≤103). Description of the t test cases follows.
The first line of each test case contains two integers n and m — size of the chessboard and the number of rooks (2≤n≤105, 1≤m<n). Each of the next m lines contains two integers xi and yi — positions of rooks, i-th rook is placed in the cell (xi,yi) (1≤xi,yi≤n). It’s guaranteed that no two rooks attack each other in the initial placement.
The sum of n over all test cases does not exceed 105.

Output

For each of t test cases print a single integer — the minimum number of moves required to place all the rooks on the main diagonal.
It can be proved that this is always possible.

Example

input
4
3 1
2 3
3 2
2 1
1 2
5 3
2 3
3 1
1 2
5 4
4 5
5 1
2 2
3 3
output
1
3
4
2

Note

Possible moves for the first three test cases:
(2,3)→(2,2)
(2,1)→(2,3), (1,2)→(1,1), (2,3)→(2,2)
(2,3)→(2,4), (2,4)→(4,4), (3,1)→(3,3), (1,2)→(1,1)

题目大意

有一个n*n的棋盘,其中有m个车(象棋里的车)。求在这些车不会相互攻击到的情况下,将它们全部移到右对角线(即所有横坐标等于纵坐标的点)上需要的最小步数。

题目分析

在这个题中,最好的情况就是一步就能移动到对角线上。但是有些时候某些点并不能一次就移到对角线上,如(1,2)和(2,1)。

我们可以将所有点 ( x , y ) (x,y) (x,y) 拆成一条 x − y x-y xy 的无向边。如果某些点变成的边连成了一个环,那么这个环上就必须有一个点要移动两次才能到达对角线上
因为此时的点(x,y)不论是移动到(x,x)还是(y,y)上都会被其它的车攻击到。因此需要有某个点先走一步离开环(先将环破坏),再移动到对角线上

这样我们就只需要找出所有点对构成的图中有多少环即可。最后的答案即为:不在对角线上的点数(如果某点一开始就在对角线上,那么连一步移动也省了)+图的环数。

找环我们可以用并查集来实现。每读入一个点(x,y),我们就用并查集合并x和y。但是如果x和y已经在一个集合中了,那就说明加入该点(x,y)之后构成了一个环。

代码如下
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <unordered_map>
#include <queue>
#include <vector>
#include <set>
#include <bitset>
#include <algorithm>
#define LL long long
#define PII pair<int,int>
#define x first
#define y second
using namespace std;
const int N=2e5+5,mod=1e9+7;
int p[N];
int find(int x)			//并查集模板
{
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) p[i]=i;		//初始化
		int ans=0;
		for(int i=1;i<=m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			if(x==y) continue;				//如果x和y一开始就在对角线上,那么一步移动也不用了
			x=find(x),y=find(y);
			if(x==y) ans++;				//如果x和y在一个集合中,说明构成了环,答案+1
			else p[x]=y;				//否则将它们合并到一个集合中
			ans++;						//加上一步移动
		}
		printf("%d\n",ans);
	}
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwz_159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值