1. 程式人生 > >Wannafly挑戰賽12 D矩陣計數

Wannafly挑戰賽12 D矩陣計數

namespace AC i++ bool operator 求和 Y軸 pan 還要

Wannafly挑戰賽12 D矩陣計數

  • 題意:給一個n x m的01矩陣,其中C個格子是1,其余全是0。求有多少全0的子矩陣。
    答案對\(10^9+7\)取模。

  • 思路:首先,全0的子矩陣=所有子矩陣-含1的子矩陣, 我們只需要對所有的值為1的格子,求出包含這個點的子矩陣數量\(s_i\)(還要保證不重復計數),那麽\(ans = \sum_{i=1}^{C}s_i\)

  • 如何求\(s_i\)
    • 首先來看一個例子,如圖:
      技術分享圖片

    • 對於所有值為1的格子,我們先排序(先按x軸再按y軸排),對於上圖中的第一個點(2, 2),先計算包含這個點的所有子矩陣,顯然是\((x-1+1)*(y-1+1)*(n-x)*(m-y+1)=2*2*4*4=64\)
      ,(x-1+1),(y-1+1),(n-x),(m-y+1)分別代表這個點距上邊界,左邊界,下邊界,右邊界的距離;
    • 再看第二個點, 由於要保證不會重復計數,計算\(s_2\)時不能包含第一個點,所以需要計算的部分如下圖:
      技術分享圖片

    • 第三個點:
      技術分享圖片

    • 總結:對於每個點,我們可以找到它再每一行可以拓展到的最大左右區間,然後乘以這個區間的高度和這個點到最後一行的距離,然後求和即是我們要求的\(s_i\)
  • Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5005;
const
ll mod = 1e9+7; int n, m, c; struct P{ int x, y; P() {} P(int x, int y): x(x), y(y) {} bool operator < (const P &t) const { return x == t.x ? y < t.y : x < t.x; } } p[maxn]; int main() { while(~scanf("%d %d %d", &n, &m, &c)) { for
(int i = 1; i <= c; i++) { scanf("%d %d", &p[i].x, &p[i].y); } sort(p + 1, p + c + 1); ll ans = 0; for(int i = 1; i <= c; i++) { for(int j = i - 1, l = 1, r = m; j >= 0; j--) { if(p[j+1].x != p[j].x) ans=(ans+(n-p[i].x+1LL)%mod*(r-p[i].y+1)%mod*(p[i].y-l+1)%mod*(p[j+1].x-p[j].x)%mod)%mod; if(p[j].y < p[i].y) l = max(l, p[j].y+1); if(p[j].y > p[i].y) r = min(r, p[j].y-1); } } printf("%lld\n", ((n*(n+1LL)/2)%mod*((m*(m+1LL)/2)%mod)%mod-ans+mod) % mod); } return 0; }

Wannafly挑戰賽12 D矩陣計數