【JZOJ1404】菱形内的计数【并查集】
题目:
题目链接:https://jzoj.net/senior/#main/show/1404
给出一个菱形,问这个菱形中有多少个内部不含边的平行四边形。
思路:
我们先把这个菱形转换为正方形。例如样例的转换方式如下:
那么接下来我们就是要求有多少个中间没有边的矩形。
我们可以记录每一个矩形的大小和最右上方和最左下方的点,这样就可以算出每一个矩形的面积。最后枚举每一个矩形,如果满足,那么这就是一个矩形。
如何判断矩形中间有没有边?我们只要枚举每一条边,如果这一条边的两边在一个连通块内,那么就将这一个连通块标记一下即可。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=800010,M=900;
int n,x,y,p1,p2,ans,father[N],MAXx[N],MINx[N],MAXy[N],MINy[N],size[N];
char c,ch[M*2][M*2];
// \92 /47
int com(int x,int y)
{
return (x-1)*n+y;
}
int find(int x)
{
return x==father[x]?x:father[x]=find(father[x]);
}
void merge(int x,int y)
{
x=find(x),y=find(y);
if (x==y) return;
MAXx[x]=max(MAXx[x],MAXx[y]);
MAXy[x]=max(MAXy[x],MAXy[y]);
MINx[x]=min(MINx[x],MINx[y]);
MINy[x]=min(MINy[x],MINy[y]);
size[x]+=size[y];
father[y]=x;
}
int main()
{
scanf("%d\n",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
int p=com(i,j);
father[p]=p; size[p]=1;
MAXx[p]=i+1; MINx[p]=i;
MAXy[p]=j+1; MINy[p]=j;
}
for (int i=1;i<=n*2;i++)
{
int j=0;
while (c=getchar())
{
if (c=='\n') break;
ch[i][++j]=c;
}
}
for (int i=1;i<=n;i++)
for (int j=2;j<=n;j++)
{
x=i+j-1; y=n+1-i+j-1;
if (ch[x][y]==' ') merge(com(i,j),com(i,j-1));
}
for (int i=1;i<=n;i++)
for (int j=2;j<=n;j++)
{
x=i+j-1; y=n+2*i-x;
if (ch[x][y]==' ') merge(com(j,i),com(j-1,i));
}
for (int i=1;i<=n;i++)
for (int j=2;j<=n;j++)
{
x=i+j-1; y=n+1-i+j-1;
if (ch[x][y]!=' ' && find(com(i,j))==find(com(i,j-1)))
size[find(com(i,j))]=-233;
}
for (int i=1;i<=n;i++)
for (int j=2;j<=n;j++)
{
x=i+j-1; y=n+2*i-x;
if (ch[x][y]!=' ' && find(com(j,i))==find(com(j-1,i)))
size[find(com(j,i))]=-233;
}
for (int i=1;i<=n*n;i++)
if (father[i]==i && size[i]==(MAXx[i]-MINx[i])*(MAXy[i]-MINy[i]))
ans++;
printf("%d",ans);
return 0;
}