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;}
- 2017多校联合第二场 1008题 hdu 6052 To my boyfriend 计数 单调队列
- 2017多校第二场 HDU 6052 To my boyfriend 思维,计数题
- 2017 多校训练第二场 HDU 6052 To my boyfriend
- Hdu-6052 To my boyfriend(单调栈)
- HDU 6052 To my boyfriend 期望 计数
- HDU 6052 To my boyfriend (计数)
- 【多校训练】hdu 6052 To my boyfriend 计数
- HDU6052 [2017多校联合2] To my boyfriend
- HDU-6052 To my boyfriend(单调栈)
- HDU 6052 To my boyfriend 思维 + 枚举(计数)
- HDU 6052 To my boyfriend
- hdu--6052--To my boyfriend
- hdu 6052 To my boyfriend
- To my boyfriend HDU
- HDU-6052 To my boyfriend 思维
- HDU 6052 To my boyfriend【思维】
- HDU 6052 To my boyfriend(分块+容斥原理+单调栈)
- 17暑假多校联赛2.8 HDU 6052 To my boyfriend
- 【Linux】一些好玩的shell脚本
- background-size(对响应性图片等比例缩放)
- spring-cxf集成rest实现webservice以及调用过程
- JSON数据格式学习笔记1
- 应用基本原理
- 2017多校联合第二场 1008题 hdu 6052 To my boyfriend 计数 单调队列
- hdu5273 Abandoned country 最小生成树 概率 Kruskal算法 前向星存图
- tensorflow训练cnn网络实现避障与导航(二)V-rep仿真环境的搭建
- the way
- 使用pip默认源下载慢,修改为国内源
- 蒜头君的树
- 单轨迹传感器巡线
- PHP与ajax的长轮询
- 阿里云效团队大规模代码构建技术实践