求出每个点向上下左右能延伸的最大长度$left$、$right$、$up$、$down$。
枚举每一条对角线,如果$j$可以作为左上角,$i$可以作为右下角,那么有:
$j+\min(down[j],right[j])-1\geq i$
$i-\min(left[i],up[i])+1\leq j$
$j<i$
排序+树状数组统计即可。
时间复杂度$O(n^2\log n)$。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2010;
int n,m,i,j,a[N][N],up[N][N],left[N][N],down[N][N],right[N][N],bit[N],ans;
struct P{int x,y;P(){}P(int _x,int _y){x=_x,y=_y;}}e[N],q[N];
inline bool cmp(const P&a,const P&b){return a.x<b.x;}
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
inline void init(){
int A,B,C,D;
read(A),read(B),read(C),read(D);
A++;B++;C++;D++;
A<<=1;B<<=1;C<<=1;D<<=1;
a[A][B]++;
a[A][D+1]--;
a[C+1][B]--;
a[C+1][D+1]++;
}
inline void add(int x){for(;x<=n;x+=x&-x)bit[x]++;}
inline void askadd(int x){for(;x;x-=x&-x)ans+=bit[x];}
inline void askdel(int x){for(;x;x-=x&-x)ans-=bit[x];}
inline void solve(int x,int y){
for(m=0;x<=n&&y<=n;x++,y++){
if(!a[x][y])continue;
e[++m]=P(x-min(up[x][y],left[x][y])+1,x);
q[m]=P(x,x+min(down[x][y],right[x][y])-1);
}
sort(e+1,e+m+1,cmp);
sort(q+1,q+m+1,cmp);
for(int i=1;i<=n;i++)bit[i]=0;
for(int i=1,j=1;i<=m;i++){
while(j<=m&&e[j].x<=q[i].x)add(e[j++].y);
askadd(q[i].y);
askdel(q[i].x);
}
}
int main(){
read(n),read(m);n++;n<<=1;
while(m--)init();
for(i=1;i<=n;i++)for(j=1;j<=n;j++)a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
for(i=1;i<=n;i++)for(j=1;j<=n;j++){
up[i][j]=a[i][j]?up[i-1][j]+1:0;
left[i][j]=a[i][j]?left[i][j-1]+1:0;
}
for(i=n;i;i--)for(j=n;j;j--){
down[i][j]=a[i][j]?down[i+1][j]+1:0;
right[i][j]=a[i][j]?right[i][j+1]+1:0;
}
for(i=1;i<=n;i++)solve(1,i);
for(i=2;i<=n;i++)solve(i,1);
return printf("%d",ans),0;
}
原文:http://www.cnblogs.com/clrs97/p/6354352.html