Description
给出一个 n∗m 的棋盘, 可以在格子里放小球,但是需要保证任意两个小球不可达,一个小球每次可以选择东南,东北,西南,西北四个方向中的一个运动,遇到边界就反弹,遇到角就原路返回,如果若干次反弹后可以到达另一个小球的位置则称这两个小球互相可达。问最多可以放多少小球
Input
两个整数 n,m 表示棋盘规模 (2≤n,m≤106)
Output
输出最多可以放的小球数量
Sample Input
3 4
Sample Output
2
Solution
如果两个小球可以到达边界的同一位置那么显然这两个小球互相可达,如果两个小球互相可达,那么这两个小球也必然可以到达边界的同一位置,故两个小球可达的边界位置不同等价于这两个小球不可达,用并查集维护 2m+2n−4 个边界位置的可达关系,最终得到的集合数即为最多可以放置的小球数
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=4000005;
int n,m,fa[maxn];
int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void unite(int x,int y)
{
x=find(x),y=find(y);
if(x==y)return ;
fa[x]=y;
}
int ID(int x,int y)
{
if(x==1)return y;
if(y==m)return m+x-1;
if(x==n)return 2*m+n-1-y;
return 2*m+2*n-2-x;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=2*m+2*n-4;i++)fa[i]=i;
if(n<m)swap(n,m);
for(int i=1;i<=n;i++)
{
if(i<=m)unite(ID(i,1),ID(1,i)),unite(ID(i,m),ID(1,m-i+1));
else unite(ID(i,1),ID(i-m+1,m)),unite(ID(i,m),ID(i-m+1,1));
if(i>=n-m+1)unite(ID(i,1),ID(n,n-i+1)),unite(ID(i,m),ID(n,m-n+i));
else unite(ID(i,1),ID(m+i-1,m)),unite(ID(i,m),ID(m+i-1,1));
}
int ans=0;
for(int i=1;i<=2*m+2*n-4;i++)
if(fa[i]==i)ans++;
printf("%d\n",ans);
}
return 0;
}