矩阵计数

 

ps:花了很久才看懂别人的暴力写法。不考虑有黑点的矩形,以 ( i,j )为右下角高度为1, 2,3, ······,i 的矩形分别都有 j 个

 当有黑点时,就要考虑枚举的高度是否是合法,比如说高度为2的矩形,但高度为1的矩形中有黑点,那么高度为2的矩形就一定不会是 j 个。

const int N = 100005;

int n, m, k, cas, up[105];
bool sp[N][105];

int main()
{
    cas = 1;
    BEGIN() {
        mem(sp, 0);
        mem(up, 0);
        
        sc(n), sc(m), sc(k);
        Rep(i, 1, k) {
            int a, b;
            sc(a), sc(b);
            sp[a][b] = 1;
        }
        LL ans = 0;
        Rep(i, 1, n) {
            Rep(j, 1, m) if (sp[i][j]) up[j] = i;
            Rep(j, 1, m) {
                int h = 0;
                for (int k = j; k; --k) {
                    h = max(h, up[k]);
                    ans += (i - h);
                }
            }
        }
        printf("Case #%d: %lld\n", cas++, ans);
    }
    return 0;
}

 ps:单调栈优化并维护前缀和(既存在递推关系)

枚举到第 5 行,维护一个单调非严格递增的栈,假设已经算出了以(5,j)为右下角的矩形个数,怎么求(5,j + 1)为右下角的矩形个数呢?设 j + 1 列的高度(从当前位置开始向上走直到遇到第一个黑色位置或者边界的距离)为h,如果 h >= S.top(),那么新增的矩形个数就是d,既sum(j + 1) = sum(j) + d;如果 h < S.top(),那么新增的个数是在栈中比h大的元素个数乘以h。因为以h为高的矩形是可以向左扩展的。

看图:sum( j = 5 ) = sum( j = 2) + 3 * h.(以(6,5)为右下角高度为h的矩形有三个)

注意:①搞清楚递推关系。②弹出多少个数就要压入多少个数。

const int N = 100005;

int n, m, k, cas, up[105], S[105];
bool sp[N][105];

int main()
{
    cas = 1;
    BEGIN() {
        mem(sp, 0);
        mem(up, 0);

        sc(n), sc(m), sc(k);
        Rep(i, 1, k) {
            int a, b;
            sc(a), sc(b);
            sp[a][b] = 1;
        }
        LL ans = 0;
        Rep(i, 1, n) {
            LL sum = 0;
            int top = 0;
            Rep(j, 1, m) {
                if (sp[i][j]) up[j] = i;
                int d = i - up[j];
                if (!d) {
                    top = 0;
                    sum = 0;
                }
                else {
                    int cnt = 1;
                    while(top && S[top - 1] > d) {
                        sum -= S[top - 1];   // 因为是维护的前缀和,S.top() > d 的位置的贡献是无用的,得减去。这里不太好理解
                        top --;
                        cnt ++;
                    }
                    Rep(k, 1, cnt) {
                        sum += d;
                        S[top++] = d;    // 弹出多少个数就压入多少个数(一切都是为了前缀和)
                    }
                }
                ans += sum;
                /* 错误得写法,没有正确得维护前缀和
                if (sp[i][j]) {
                    up[j] = i;
                    top = 0;
                    sum = 0;
                }
                int d = i - up[j];
                int cnt = 1;
                while(top && S[top - 1] >= d) {
                    sum -= S[top - 1];
                    top--;
                    cnt++;
                }
                S[top++] = d;
                Rep(j, 1, cnt) sum += d;
                ans += sum;
                */
            }

        }
        printf("Case #%d: %lld\n", cas++, ans);
    }
    return 0;
}

 矩阵计数(牛客)

数据范围:1 <= n <= 1e9,1 <= m <= 1e9,1 <= k <= 5000

ps:显然不能用上面的方法。 k 只有5000个,所以:

点S(x,y),以(1,1)为左上角,(x,y)为右下角的矩形叫SP,和以 (x,y)为左上角,(n,m)为右下角的矩形叫SQ,假设 点P∈SP,点Q∈SQ,那么以P点为左上角,Q点为右下角形成的矩形一定包括点S。讨论矩形SP,并计算包含第 i 个点(点S)的矩形个数:记 F ( 矩形X)= cnt(矩形X包含点的个数),首先点集{1 ~ ( i - 1)}可以分为两部分,一部分点在SP内,另一部分不在SP内:{B1,B2,B3},{B4}。仔细观察SQ矩形内的点在SP内那部分的点会产生贡献,

那么包含点S的矩形个数 ans = F( A1 + A2 ) * F( C1) + F( A1 ) * F( C2 ) + F( A1 ) * F( C3 ) + F( A1 ) * F( C4 ) + F( A1 ) * F( C5 )。既SQ内的点(作为矩形的右下角)只会和有颜色那部分的点(作为矩形的左上角)形成的矩形才是去重后包含S点矩形的个数。且不在SP内的点集{B4}会限制SQ矩形的宽度(矩形A2内的点的贡献在点B4就已经计算过)。

const int mod = 1000000007;

P p[5005];
int n, m, C;

bool cmp(P x, P y) {
    if (x.first == y.first) return x.second < y.second;
    return x.first < y.first;
}

int main()
{
    sc(n), sc(m), sc(C);
    Rep(i, 1, C) sc(p[i].first), sc(p[i].second);
    sort(p + 1, p + C + 1, cmp);

    LL ans = 0;
    Rep(i, 1, C) {
        int l = 0, r = m + 1;
        for (int j = i - 1; ~j; --j) {
            ans += 1ll * (p[i].second - l) * (r - p[i].second) % mod * 1ll * (p[j + 1].first - p[j].first) % mod * (n - p[i].first + 1) % mod;
            ans %= mod;
            if (p[j].second > p[i].second) r = min(r, p[j].second);
            if (p[j].second < p[i].second) l = max(l, p[j].second);
        }
    }
    LL res = ((1ll * n * (n + 1) / 2) % mod) * ((1ll * (m + 1) * m / 2) % mod) % mod;
    cout << (res - ans + mod) % mod << endl;
    return 0;
}

如果看懂了dp的做法,就能去做 51Nod 1291了。orzzzzz!

posted @ 2018-09-05 00:31  天之道,利而不害  阅读(613)  评论(0编辑  收藏  举报