BZOJ2509 : 送分题

求出每个点向上下左右能延伸的最大长度$left$、$right$、$up$、$down$。

枚举每一条对角线,如果$j$可以作为左上角,$i$可以作为右下角,那么有:

$j+min(down[j],right[j])-1geq i$

$i-min(left[i],up[i])+1leq j$

$j<i$

排序+树状数组统计即可。

时间复杂度$O(n^2log 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;
}

  

原文地址:https://www.cnblogs.com/clrs97/p/6354352.html