考慮枚舉正方形左上角右下角所在的對角線,首先我們可以預處理出每個格子的X值和Y值,X[i][j]代表從(i,j)這格往右往下長度X[i][j]以內的格子都是1(或是(i,j),...,(i,j+X[i][j]-1)均為1,往下同理),Y[i][j]則是代表往左往上的,並且兩者都是取最大的值。那麼當我們枚舉一條對角線時,記這條對角線上第i格的X,Y值分別為x[i],y[i],那我們想找的就是所有二元組(i,j)的數量(i\leq j),滿足x[i]\geq j-i+1,且y[j]\geq j-i+1。注意到這條式子中,不管怎麼樣當i>j時他一定會成立,因此我們可以把i\leq j這個條件拿掉,再扣掉多算到的部份就可以了。將上式移項得i+x[i]\geq j+1,i\geq j+1-y[j],那麼就可以轉換成簡單的問題了:考慮平面上的n個紅點和n個藍點,其中第i個紅點的座標為(i+x[i],i),第i個藍點的座標為(i+1,i+1-y[i]),求滿足「紅點的兩座標均\geq藍點的兩座標」的(紅點、藍點)的個數(或是說紅點包住藍點的點對數量)。這可以先把所有點按照x座標排序,再用個 BIT 查詢每個紅點包住了幾個藍點就可以了。
code :
#include<bits/stdc++.h> #define lowbit(x) ((x)&(-(x))) using namespace std; const int maxn=1000+10 ; struct event { int x,y,type ; bool operator < (const event &rhs) const { return x==rhs.x ? type<rhs.type : x<rhs.x ; } }; int bit[maxn] ; void add(int x) { for(;x<maxn;x+=lowbit(x)) bit[x]++ ; } int query(int x) { int ret=0 ; for(;x;x-=lowbit(x)) ret+=bit[x] ; return ret ; } char G[maxn][maxn] ; int u[maxn][maxn],d[maxn][maxn],l[maxn][maxn],r[maxn][maxn] ; vector<event> v[2*maxn] ; main() { int n ; scanf("%d",&n) ; for(int i=1;i<=n;i++) scanf("%s",G[i]+1) ; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) u[i][j]=(G[i][j]=='1' ? u[i-1][j]+1 : 0) , l[i][j]=(G[i][j]=='1' ? l[i][j-1]+1 : 0) ; for(int i=n;i>=1;i--) for(int j=n;j>=1;j--) d[i][j]=(G[i][j]=='1' ? d[i+1][j]+1 : 0) , r[i][j]=(G[i][j]=='1' ? r[i][j+1]+1 : 0) ; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) v[i-j+n].push_back((event){i,i+1-min(u[i][j],l[i][j]),1}) , v[i-j+n].push_back((event){i+min(r[i][j],d[i][j]),i,2}) ; int ans=0 ; for(int i=1;i<2*n;i++) { sort(v[i].begin(),v[i].end()) ; memset(bit,0,sizeof(bit)) ; for(auto j : v[i]) { if(j.type==1) add(j.y) ; else ans+=query(j.y) ; } int sz=v[i].size()/2 ; ans-=sz*(sz-1)/2 ; } printf("%d\n",ans) ; }
沒有留言:
張貼留言