HDU 4331 Image Recognition [边上全为1构成的正方形个数]

  首先能够构成正方形的两个对顶点肯定在同一条对角线上,可以想到依次处理每条对角线上的点。

  对于每个点,预处理出它能到的四个方向的连续1最多有多少个,记为p_up,p_down,p_left,p_right,显然,决定某一个点向右下能延伸的多少只取决于p_down和p_right中的较小值,决定某一个点向左上能延伸到多少只取决于p_up和p_left的较小值。

  

  对于每个点,记录它能到达的最右的x轴的绝对坐标,并记录它自身的y坐标。

  统计一个点和它左上的点(在对角线上)能组成多少个正方形时,只要判断有多少个点向右能和它相交即可。比如统计5,只要记录1~4中有多少点向右能和红线交叉,显然5可以和1,3,4组成边上全部为1的正方形。

  HDU给的题解没太看懂,我是用一个堆维护每个点能到的最右的x轴坐标,当堆顶元素向右不能到达该点的x坐标时,就可以将这个点删除,因为这个点已经没有用了,比如统计5时,就可以将2这个点删除,它已经不能和5以及以后的点组成正方形,然后再统计红线范围内有多少点即可,这里用树状数组线段树什么的都可以。

  另外这题如果不一条条对角线分别统计会MLE。。。可能是以为我用了堆的缘故吧。。

 

 1 #include <string.h>
 2 #include <stdio.h>
 3 #include <queue>
 4 #define MAXN 1005
 5 using namespace std;
 6 typedef pair<int,int> pii;
 7 typedef __int64 LL;
 8 int cas,n,mz[MAXN][MAXN],d[MAXN][MAXN][4];
 9 int c[MAXN];
10 int lowbit(int x){return x&-x;}
11 void update(int k,int d){
12     while(k<n+1)c[k]+=d,k+=lowbit(k);
13 }
14 int getans(int k){
15     int ret=0;
16     while(k)ret+=c[k],k-=lowbit(k);
17     return ret;
18 }
19 int main(){
20     //freopen("test.in","r",stdin);
21     scanf("%d",&cas);
22     for(int ca=1;ca<=cas;ca++){
23         scanf("%d",&n);
24         memset(mz,0,sizeof mz);
25         for(int i=1;i<=n;i++)
26             for(int j=1;j<=n;j++)
27                 scanf("%d",&mz[i][j]);
28         //计算最长能延伸到4个方向的距离
29         //0:left-> 1:right<- 2:top 3:bottom
30         for(int i=1;i<=n;i++)
31             for(int j=1;j<=n;j++)
32                 d[i][j][0]=mz[i][j]?d[i][j-1][0]+1:0,
33                 d[i][j][2]=mz[i][j]?d[i-1][j][2]+1:0;
34         for(int i=n;i>=1;i--)
35             for(int j=n;j>=1;j--)
36                 d[i][j][1]=mz[i][j]?d[i][j+1][1]+1:0,
37                 d[i][j][3]=mz[i][j]?d[i+1][j][3]+1:0;
38         LL ans=0;
39         //枚举对角线,能构成正方形的点的对顶点必然在一条对角线上
40         for(int dt=-n+1;dt<=n-1;dt++){
41             //用堆维护该对角线上向左和向右都能延伸到当前点的集合
42             //并用树状数组统计某个区间内能到达该点的点的数量
43             priority_queue<pii,vector<pii>,greater<pii> > q;
44             memset(c,0,sizeof c);
45             for(int i=1-dt>0?1-dt:1;i<=n&&i+dt<=n;i++){
46                 int j=i+dt;
47                 if(mz[i][j]==0)continue;
48                 if(d[i][j][3]<d[i][j][1])d[i][j][1]=d[i][j][3];
49                 if(d[i][j][2]<d[i][j][0])d[i][j][0]=d[i][j][2];
50                 q.push(pii(j-1+d[i][j][1],i));
51                 update(i,1);
52                 while(!q.empty()&&q.top().first<j){
53                     update(q.top().second,-1);
54                     q.pop();
55                 }
56                 ans+=getans(i)-getans(i-d[i][j][0]);
57             }
58         }
59         printf("Case %d: %I64d\n",ca,ans);
60 
61 
62     }
63     return 0;
64 }
原文地址:https://www.cnblogs.com/swm8023/p/2661253.html