2017多校联合第二场 1008题 hdu 6052 To my boyfriend 计数 单调队列

来源:互联网 发布:江苏凤凰数据有限公司 编辑:程序博客网 时间:2024/06/05 13:32

题目链接


题意:

给定一个矩阵 (1 <= n, m <= 100),每个格子都有一种颜色 (0 < color < n * m),对于它的每一个子矩阵,其 value 值等于该矩阵中出现过的颜色的个数,求 value 值的期望。


推荐:

http://blog.csdn.net/calabash_boy/article/details/76272704

原Po真的每一篇都写得很详细认真哇


思路:

显然,题目要求的就是所有子矩形的 value 值的总和,题意与第一场那道 colorful tree 几乎是一模一样(除了一个设定在树上,一个设定在矩阵中)。

计数问题,还是考虑部分对整体的贡献。那么这道题里将什么作为部分 来考虑呢?


考虑一个方格对一个矩阵的贡献。假如该矩阵中有多个方格与该方格颜色相同,那么只算最上方最左边的那一个方格的贡献。

也就是说,对于某个矩阵 M 中的某颜色 c 而言,其 只会被算在 该矩阵中 出现在 最上方最左边的 颜色为 c 的 格子 p 中,

只会在算格子 p 时,算上 c 对 M 的贡献;而对于 M 中其他颜色为 c 的格子,则不再计算。


所以,现在的目标很明确了,对于给定的格子 p,我们要去算它对多少矩阵有贡献。


显然,下边界是可以一直延伸到最下方的,因为包含下方的格子不会影响 p 的最小性。

又显然,右边界与左边界是依据上边界而确定的。

对于右边界,在同一行内是可以一直向右延伸的,因为同理,包含同一行右边的格子不会影响 p 的最小性。

但是往上一行去找,其右边最远就要受到限制了;而越往上,右边最远延伸的距离是单调减的。

这里可以用单调队列去处理。维护一个单调增的队列,队头即为在该行可以向右延伸的最远距离。

至于左边界。在同一行内就要受限制,往上和右边界同理,也是用单调队列用一样的方法处理。


实现细节相关:

一开始 以行和列 循环,最开始开了一个 vector<int> color[maxn][maxn * maxn] 记录 每一行 每一种颜色 所在的列号,为了方便 每个格子的颜色 向上找时用 lower_bound 直接确定该行该颜色的左右边界,然而显然 MLE了...

于是就改成了 以颜色 为外层循环,做每一种颜色的时候再去记录 vector<int> line[maxn] 即该颜色在 每一行 的 列号,这样就过了。

其实本来写之前也就应该考虑到这一点的,原来那样记浪费的空间也实在是太多了,一种颜色一种颜色的去清,空间的利用效率就高很多了。


AC代码如下:

#include <bits/stdc++.h>#define maxn 110using namespace std;struct span { int l, r; }span[maxn];int st1[maxn], st2[maxn], color[maxn][maxn];struct pos {    int x, y;    pos(int a = 0, int b = 0) : x(a), y(b) {}};vector<pos> col[maxn * maxn];vector<int> line[maxn];typedef long long LL;void work() {    int n, m;    scanf("%d%d", &n, &m);    for (int i = 0; i < m * n; ++i) col[i].clear();    memset(span, 0, sizeof(span));    for (int i = 0; i < n; ++i) {        for (int j = 0; j < m; ++j) {            int temp;            scanf("%d", &temp);            color[i][j] = temp;            col[temp].push_back(pos(i, j));        }    }    LL ans = 0;    for (int c = 0; c < m * n; ++c) {        if (col[c].empty()) continue;        for (int i = 0; i < n; ++i) line[i].clear();        for (auto ele : col[c]) {            line[ele.x].push_back(ele.y);        }        for (auto ele : col[c]) {            int tot1 = 0, tot2 = 0, i = ele.x, j = ele.y;            span[i].r = m - j;            auto it = lower_bound(line[i].begin(), line[i].end(), j);            if (it == line[i].begin()) span[i].l = j + 1;            else span[i].l = j - *(it - 1);            st2[tot2++] = span[i].l;            for (int k = i - 1; k >= 0; --k) {                auto it = lower_bound(line[k].begin(), line[k].end(), j);                int diff1;                if (it == line[k].end()) diff1 = m - j;                else diff1 = *it - j;                if (diff1 == 0) {                    span[k].r = 0;                    break;                }                while (tot1 > 0 && diff1 < st1[tot1 - 1]) --tot1;                st1[tot1++] = diff1;                span[k].r = st1[0];                int diff2;                if (it == line[k].begin()) diff2 = j + 1;                else diff2 = j - *(it - 1);                if (diff2 == 0) {                    span[k].l = 0;                    break;                }                while (tot2 > 0 && diff2 < st2[tot2 - 1]) --tot2;                st2[tot2++] = diff2;                span[k].l = st2[0];            }//            printf("i j : %d %d\n", i, j);            for (int k = i; k >= 0; --k) {//                printf("%d %d %d\n", k, span[k].l, span[k].r);                if (span[k].l == 0 || span[k].r == 0) break;                ans += (LL)span[k].l * span[k].r * (n - i);            }        }    }    int num = (n * (n + 1) >> 1) * (m * (m + 1) >> 1);//    printf("%lld %d\n", ans, num);    printf("%.9f\n", (double)ans / num);}int main() {    int T;    scanf("%d", &T);    while (T--) work();    return 0;}


原创粉丝点击