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
x−y 的无向边。如果某些点变成的边连成了一个环,那么这个环上就必须有一个点要移动两次才能到达对角线上。
因为此时的点(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;
}
标签:692,Rooks,int,查集,number,对角线,test,cases,include 来源: https://blog.csdn.net/li_wen_zhuo/article/details/113924175