描述
如圖所示,在由N行M列個單位正方形組成的矩形中,有K個單位正方形是黑色的,其餘單位正方形是白色的。
你能統計出一共有多少個不同的子矩形是完全由白色單位正方形組成的嗎?
輸入
第一行三個整數:N, M和K。
之後K行每行包括兩個整數R和C,代表一個黑色單位正方形的位置。
1 <= N,M <= 1000
1 <= K <= 10
1 <= R <= N
1 <= C <= M
輸出
輸出一個整數表示滿足條件的子矩形個數。
3 3 1 2 3樣例輸出
24
思路
- 我們通過dp來求解這個問題。我們定義dp(i,j)表示前以(1, 1)作爲左上角,(i, j)作爲右下角構成的子矩陣所具有的矩形個數。
- 遞推的時候,基於簡單的容斥原理思想,不難發現:dp(i,j) = dp(i-1, j) + dp(i, j-1) - dp(i-1, j-1) + 用(i, j)作爲右下角的子矩形個數
- 遞推式的前三項的運算得到的是不用(i, j)作爲右下角的子矩形個數,而最後一項則需要稍微複雜一些的計算得到。
- 注意到:用(i, j)作爲右下角的子矩形個數 = 可以作爲左上角的小格子個數,而“ 可以作爲左上角的小格子個數”受(i, j)左上方的小黑格影響。
- 若(i, j)左上方只有一個小黑格b1位於(x1, y1),那麼“ 可以作爲左上角的小格子個數” = i * j - x1 * y1
- 若還有一個小黑格b2位於(x2, y2),若b2 處於 b1的左上方(稱爲b2被b1遮擋),即x2 < x1, y2 < y1,則b2就不會發揮任何作用,即“ 可以作爲左上角的小格子個數” 依然等於 i * j - x1 * y1
- 因此,我們只需關心互相不被遮擋的且位於(i, j)左上方的小黑格即可,通過畫圖不難發現,這些小黑格一定位於不同行和不同列上,佈局類似反對角線,當我們找到這些小黑格後,計算“ 可以作爲左上角的小格子個數” 就比較簡單了,式子就不再列出了,可以參看後面的源碼。
- 所以,接下來的問題就是如何找到這些互不遮擋的小黑格了。由於小黑格個數很少,我們可以遍歷它們。具體方法應該挺多的,這裏我爲了實現簡單一些,使用了類似單調棧的思想。預處理所有小黑格,以行x爲第一優先級,列y爲第二優先級,升序排序,這樣我保證在遍歷的時候,後面的小黑格一定不會被前面的小黑格遮擋。再加上這些互不遮擋的小黑格滿足這樣的性質:任取其中兩個小黑格(xi, yi)和(xj, yj)若xi < xj, 則yi > yj。我們制定如下策略,當遍歷到bi,假如棧空或bi不遮擋棧頂元素,就bi入棧;若遮擋,則彈出棧頂,繼續判斷,直至不遮擋或棧空爲止,把bi入棧。
- 另外這個題還可以通過容斥原理求解,大家可以參看:點擊打開鏈接
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
typedef long long ll;
typedef pair<int, int> pii;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
ll dp[maxn][maxn];
vector<pii> vec;
stack<pii> sta;
int n,m,k;
int main(){
ios::sync_with_stdio(false);
cin>>n>>m>>k;
for (int i=0;i<k;i++){
int x,y;
cin>>x>>y;
dp[x][y] = 1;
vec.pb(mp(x,y));
}
sort(vec.begin(),vec.end());
int cnt = 0;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
bool flag = dp[i][j];
dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1];
if (flag)
{
cnt += flag;
continue;
}
for (int t=0;t<cnt;t++){
if (vec[t].fi > i || vec[t].se > j){
continue;
}
while (sta.size() && sta.top().se <= vec[t].se){
sta.pop();
}
sta.push(vec[t]);
}
ll sum = i * j;
int pre_y = 0;
while (sta.size()){
int x = sta.top().fi;
int y = sta.top().se;
sta.pop();
sum -= (y-pre_y) * x;
pre_y = y;
}
dp[i][j] += sum;
}
}
cout << dp[n][m] << endl;
return 0;
}