1. 程式人生 > >01矩陣計數(帶禁手)

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 }