01矩阵系列(计数/最大查询)
51Nod 1291
题意:600*600的01矩阵,统计宽i高j的全1矩阵的个数。
题解:枚举矩阵的下边界,对于每个下边界,统计所有宽极大的矩形的答案(高度可以用差分)。O(nm)的复杂度统计完之后,我们已知所有高度的宽极大的答案,列一下式子发现两次前缀和就是最后答案。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define st first 4 #define nd second 5 #define rep(i, a, b) for(int i=(a); i<(b); i++) 6 #define sz(x) (int)x.size() 7 #define de(x) cout<< #x<<" = "<<x<<endl 8 #define dd(x) cout<< #x<<" = "<<x<<" " 9 typedef long long ll; 10 typedef pair<int, int> pii; 11 typedef vector<int> vi; 12 13 const int N = 666; 14 int n, m, top; 15 int u[N], sta[N]; 16 ll c[N][N]; 17 char s[N]; 18 19 int main() { 20 scanf("%d%d", &n, &m); 21 for(int i = 1; i <= n; i++) { 22 scanf("%s", s+1); 23 for(int j = 1; j <= m; j++) 24 u[j] = (s[j] == '1')? u[j]+1: 0; 25 top = 0; 26 sta[top++]=0; 27 for(int j = 1; j <= m+1; j++) { 28 while(u[sta[top-1]] > u[j]) { 29 ++c[max(u[sta[top-2]], u[j])+1][j-sta[top-2]-1];//维护单调上升的栈, 看每个柱块向左和向右的最大延伸距离, 即为宽度 30 --c[u[sta[top-1]]+1][j-sta[top-2]-1]; //枚举i为底边, 对高度范围为[max(u[sta[top-2]],u[j])+1, u[sta[top-1]]], 宽度为j-sta[top-2]-1的矩形加1 31 --top; 32 } 33 while(top && u[sta[top-1]] == u[j]) --top; 34 sta[top++] = j; 35 } 36 } 37 for(int i = 2; i <= n; i++) for(int j = 1; j <= m; j++) c[i][j] += c[i-1][j]; //c1[i, j]: 高为i (n^2) 的连通块, 只统计最长宽度的。 38 for(int i = 1; i <= n; i++) { 39 for(int j = m-1; j; j--) c[i][j] += c[i][j+1]; //c2[i, j]: 高i宽 >= j的连通块的个数 40 for(int j = m-1; j; j--) c[i][j] += c[i][j+1]; //c3[i, j]: 高i宽j的全1矩阵的个数 41 } 42 for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) printf("%lld%c", c[i][j], " \n"[j == m]); 43 return 0; 44 } 45 /* 46 3 3 47 011 48 110 49 110 50 如何得到c1? 暴力的话, c1是枚举n^2的上下边界, 优化后, 变成枚举直方图的最底边,快速统计各个不同的上顶边。这个可以通过单调栈+差分解决。求前缀和后就得到c1。 51 总的时间复杂度为O(nm). 52 c1 53 0 3 0 54 1 1 0 55 1 0 0 56 c2 57 3 3 0 58 2 1 0 59 1 0 0 60 c3 61 6 3 0 62 3 1 0 63 1 0 0 64 65 */
Wannafly挑战赛12 D
题意:1e9*1e9的01矩阵,1的个数c个(c <= 5000),统计全0矩阵的个数。
题解:所有情况 减去 包含1的。包含1的矩阵这么算:对于每个1,统计包含这个1而不包含之前的1的矩阵个数。思想类似于带禁手的马跳棋盘方案数计数。时间复杂度 O(c^2)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define st first 4 #define nd second 5 typedef long long ll; 6 const int mod = 1e9+7; 7 pair<int, int> s[5111]; 8 int main() { 9 int n, m, c; 10 scanf("%d%d%d", &n, &m, &c); 11 for(int i = 1, x, y; i <= c; i++) scanf("%d%d", &x, &y), s[i] = {x, y}; 12 sort(s+1, s+c+1); 13 long long ans = 0; 14 for(int i = 1; i <= c; i++){ 15 int l = 0, r = m+1; 16 for(int j = i-1; ~j; j--){ //从下往上从左往右枚举之前的点 17 ans += (r-s[i].nd)*(n-s[i].st+1LL)%mod*(s[j+1].st-s[j].st)%mod*(s[i].nd-l)%mod; //上边界为s[j].st ~ s[j+1].st 18 ans %= mod; 19 if(s[j].nd > s[i].nd) r = min(r, s[j].nd); 20 if(s[j].nd < s[i].nd) l = max(l, s[j].nd); 21 } 22 } 23 //n*m矩阵的子矩阵个数 = 左右两边界 * 上下两边界 24 long long cnt = (n*(n+1LL)/2%mod)*(m*(m+1LL)/2%mod)%mod; 25 ans = (cnt-ans)%mod; 26 if(ans < 0) ans += mod; 27 printf("%lld\n", ans); 28 return 0; 29 }
Codeforces 713D Animals and Puzzle
题意:一个n*m的01矩阵,Q个询问,每次询问一个矩形区域内,最大的全1正方形的边长是多少?
题解:dp[0][0][i][j]表示以(i, j)为右下角的正方形的最长边长。RMQ后,二分答案即可。
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int N = 1e3+10; 7 int x1, y1, x2, y2, n, m; 8 int a[N][N]; 9 int dp[10][10][N][N]; 10 int Log[N]; 11 int ask(int x1, int y1, int x2, int y2){ 12 int k1 = Log[x2-x1+1], k2 = Log[y2-y1+1]; 13 int ans = max(dp[k1][k2][x1][y1], dp[k1][k2][x1][y2-(1<<k2)+1]); 14 ans = max(ans, dp[k1][k2][x2-(1<<k1)+1][y1]); 15 ans = max(ans, dp[k1][k2][x2-(1<<k1)+1][y2-(1<<k2)+1]); 16 return ans; 17 } 18 bool test(int l){ 19 //dp[0][x1][y1] dp[0][x2-x+1][y2-x+1] 20 int ret = ask(x1+l-1, y1+l-1, x2, y2); 21 return ret >= l; 22 } 23 void init(){ 24 for(int i = 1; i <= Log[n]; i++) 25 for(int x = 1; x+(1<<i)-1 <= n; x++) 26 for(int y = 1; y <= m; y++) 27 dp[i][0][x][y] = max(dp[i-1][0][x][y], dp[i-1][0][x+(1<<i-1)][y]); 28 for(int i = 0; i <= Log[n]; i++) 29 for(int j = 1; j <= Log[m]; j++) 30 for(int x = 1; x+(1<<i)-1 <= n; x++) 31 for(int y = 1; y+(1<<j)-1 <= m; y++) 32 dp[i][j][x][y] = max(dp[i][j-1][x][y], dp[i][j-1][x][y+(1<<j-1)]); 33 } 34 int main(){ 35 for(int i = 2; i < N; i++) Log[i] = Log[i>>1]+1; 36 scanf("%d%d", &n, &m); 37 for(int i = 1; i <= n; i++) 38 for(int j = 1; j <= m; j++){ 39 scanf("%d", &a[i][j]); 40 if(a[i][j] == 0) dp[0][0][i][j] = 0; 41 else{ 42 int len = min(dp[0][0][i-1][j], dp[0][0][i][j-1]); 43 dp[0][0][i][j] = len+a[i-len][j-len]; 44 } 45 } 46 init(); 47 48 int t; scanf("%d", &t); 49 while(t--){ 50 scanf("%d%d%d%d", &x1, &y1, &x2, &y2); 51 int L = 0, R = min(x2-x1+1, y2-y1+1); 52 while(L < R){ 53 int M = L+R+1 >> 1; 54 if( test(M) ) 55 L = M; 56 else 57 R = M-1; 58 } 59 printf("%d\n", L); 60 } 61 return 0; 62 }
诸神对凡人心生艳羡,厌倦天堂。