BZOJ2509 : 送分题
求出每个点向上下左右能延伸的最大长度$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; }