题目链接:http://codeforces.com/problemset/problem/364/E
时间复杂度O(n*m*K * (lgn + lgm) ) (lgn +lgm)是分治的复杂度
#include<stdio.h> #include<string.h> #include<math.h> #include<iostream> #include<algorithm> #define N 2550 #define Mid(x,y) ((x+y)>>1) using namespace std; int n, m, K; __int64 ans; int s[N][N], up[8], down[8]; inline int sum(int x1, int x2, int y1, int y2){ //求出该矩阵的1个数 return s[x2][y2] - s[x2][y1] - s[x1][y2] + s[x1][y1]; } void work(int x1, int x2, int y1, int y2, bool dir){ //横竖交替割 计算 满足条件 且在水平中线上的子矩阵个数 if(x1 == x2 || y1 == y2)return ; if((x1+1 == x2) && (y1+1 == y2)) { ans += sum(x1, x2, y1, y2) == K; return ;} if(dir)//dir == true 表示划水平线 { int mid = Mid(x1, x2); work(x1, mid, y1, y2, !dir);//分治,分别计算 水平中线 割开后的2个子矩阵 work(mid, x2, y1, y2, !dir); //先在中间作一条水平线 //然后统计跨越水平线并且恰好含有k个1的矩形有多少个 //然后这个统计过程呢就需要两个数组 for(int i = y1; i < y2; i++) { up[0] = down[0] = mid; //显然当 1个数为0个时, x坐标 == mid for(int k = 1; k <= K+1; k++) up[k] = x1, down[k] = x2; for(int j = i+1; j<=y2; j++) { //对于矩阵 (x,i)-(mid,j) 这里面1的个数 < k个时 x的坐标就是 up[k] * 并且upbarrier贴得最紧 for(int k = 1; k <= K+1; k++) while(sum(up[k], mid, i, j) >=k)up[k]++; for(int k = 1; k <= K+1; k++) while(sum(mid, down[k], i, j)>=k)down[k]--; for(int k = 0; k <= K; k++)// 此句何意 ans += (up[k] - up[k+1]) * (down[K-k+1] - down[K-k]); //那么up[k]所在矩阵 和 up[k+1] 所在矩阵 之间要么1个数相等 要么相差为1 (相等乘法就是0,相差为1就是所要结果) } } } else //划垂直线 { int mid = Mid(y1, y2); work(x1, x2, y1, mid, !dir); work(x1, x2, mid, y2, !dir); for(int i = x1; i < x2; i++) { up[0] = down[0] = mid; for(int k = 1; k <= K+1; k++) up[k] = y1, down[k] = y2; for(int j = i+1; j <= x2; j++) { for(int k = 1; k <= K+1; k++) while(sum(i, j, up[k], mid) >= k) up[k]++; for(int k = 1; k <= K+1; k++) while(sum(i, j, mid, down[k])>=k) down[k]--; for(int k = 0; k <= K; k++) ans += (up[k] - up[k+1]) * (down[K-k+1] - down[K-k]); } } } } int main(){ while(~scanf("%d %d %d",&n,&m,&K)){ memset(s[0], 0, sizeof(s[0])); for(int i = 1; i <= n; i++) { char c[N];scanf("%s",c); for(int j = 1; j <= m; j++) s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + c[j-1]-'0'; } ans = 0; work(0, n, 0, m, false); printf("%I64d\n",ans); } return 0; } /* 3 3 2 101 000 101 5 5 1 00000 00000 00100 00000 00000 5 5 6 01010 10101 01010 10101 01010 3 3 0 001 010 000 */