给定M条平行于坐标轴的线段,问共形成了多少个正方形。
所谓形成了一个正方形,是指该正方形的四条边,均可由给定线段的一部分拼接而成。
60分暴力还打挂了的成了0分的辣鸡 ...
由于是线段的问题,我们可以将这条线段的存在赋在它右边(下边)的点上,横竖分开处理出一个点向左/右/上/下延伸的最大距离为lef,rig,up,dow,注意处理右边与下面点的时候,因为点值赋在右下端,判断的是[i][j+1]或者[i+1][j]是否为1。
接着处理出来了这个东西,由于能构成正方形的一定在同一条对角线上,我们可以通过枚举对角线,再枚举其对角线上之前的点,判断当前点的lef与up的min值,是否能包括其枚举的上一个点,以及枚举的上一个点的rig与dow值,能否覆盖当前点,如果都可以则是一组可行解了。枚举点n^2,枚举同一条对角线上的另外一个点o(n) 总复杂度n^3。
那么如何优化呢,可以使用树状数组,具体操作如下
对于每次枚举的时候,我们第一步查询它左上覆盖范围内,有多少加入了的点,即为这个点的答案。第二步将它加入到树状数组中,第三步在它超出了它右下的最大覆盖范围时,将它从树状数组中删去。
这里我(题解)的做法是将每一个点拆成三个点,并建立结构体nod (flag,pos1,pos2) 。第一步操作记flag为-1,pos1设置为当前点的位置,pos2设置为当前点覆盖的最左上角,第二步操作记flag为0,pos1设置为当前点的位置,pos2因为只涉及插入所以随意,第三部操作记flag为1,pos1设置为当前点所覆盖的最右下角,pos2设置为当前点自己的位置(到pos1要删去的点)。那么首先按pos1排序,再按flag排序,执行上面的操作即可。注意顺序千万不能错。
下附AC代码。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 1005
using namespace std;
typedef long long ll;
int read()
{
char c;int sum=0,f=1;c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
struct nod
{
int flag,pos1,pos2;
nod(int a,int b,int c)
{
flag=a;pos1=b;pos2=c;
}
nod(){}
}temp[maxn];
bool operator<(nod a,nod b)
{
return a.pos1!=b.pos1 ? a.pos1<b.pos1 : a.flag<b.flag;
}
int n,m;
ll ans=0;
int dat[maxn];
int a[maxn][maxn],b[maxn][maxn];
int lef[maxn][maxn],rig[maxn][maxn],up[maxn][maxn],dow[maxn][maxn];
int lowbit(int now)
{
return (now&(-now));
}
void add(int now,int val)
{
for(int i=now;i<=n;i+=lowbit(i))
dat[i]+=val;
}
int sum(int now)
{
int res=0;
for(int i=now;i>0;i-=lowbit(i))
res+=dat[i];
return res;
}
void solve(int x,int y)
{
int tot=0;
for(int i=0;i+x<=n && i+y<=n ;i++)
{
temp[++tot]=nod(-1,i+x,i+x-min(lef[i+x][i+y],up[i+x][i+y]));
temp[++tot]=nod(0,i+x,0);
temp[++tot]=nod(1,i+x+min(rig[i+x][i+y],dow[i+x][i+y]),i+x);
}
sort(temp+1,temp+1+tot);
memset(dat,0,sizeof(dat));
for(int i=1;i<=tot;i++)
{
switch(temp[i].flag)
{
case -1 : ans+=sum(n)-sum(temp[i].pos2-1);break;
case 0 : add(temp[i].pos1,1);break;
case 1 : add(temp[i].pos2,-1);break;
}
}
}
int main()
{
n=read();m=read();n++;
for(int i=1;i<=m;i++)
{
int x1,y1,x2,y2;
x1=read();y1=read();x2=read();y2=read();
x1++;y1++;x2++;y2++;
if(x1==x2) for(int j=y1+1;j<=y2;j++) a[x1][j]=1;
else for(int j=x1+1;j<=x2;j++) b[j][y1]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(a[i][j]) lef[i][j]=lef[i][j-1]+1;
if(b[i][j]) up[i][j]=up[i-1][j]+1;
}
}
for(int i=n;i>=1;i--)
{
for(int j=n;j>=1;j--)
{
if(a[i][j+1]) rig[i][j]=rig[i][j+1]+1;
if(b[i+1][j]) dow[i][j]=dow[i+1][j]+1;
}
}
solve(1,1);
for(int i=2;i<=n;i++)
{
solve(1,i);solve(i,1);
}
printf("%lld\n",ans);
}