专栏文章
题解: AT_abc391_d [ABC391D] Gravity
AT_abc391_d题解参与者 27已保存评论 29
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 29 条
- 当前快照
- 1 份
- 快照标识符
- @miqcvffy
- 此快照首次捕获于
- 2025/12/04 02:43 3 个月前
- 此快照最后确认于
- 2025/12/04 02:43 3 个月前
解题思路
注意到落下的正方形们满一行才会被消除,之后的正方形才会落下来。所以能清除多少行正方形和正方形们初始的高度无关,而是等于同一个 中最少有多少个正方形。如图 所示,没有正方形会被消除:

在加入一个绿色正方形后,最底部的一行才会被消除,如图 所示:

我们可以使用一个
vector<int> 来存储每一列从低到高的正方形。具体的,可以按顺序输入到两个
pair<int, int> 数组 和 ,将 输入到 ();将 输入到 ,再将 拷贝一份到 ,以便后用。(当然输入到 ,拷贝到 也是一样的。)再将 按 不降序排序,这需要自定义比较函数
CPPbool cmp(pair<int, int> x, pair<int, int> y) {
return x.second < y.second;
}
再使用
sort(a + 1, a + n + 1, cmp) 进行排序。当然,你要愿意手搓 我也不会拦你。然后按顺序遍历 数组,,在 的尾部追加 ,这样保证了表示每一列的向量中,下标越大的正方形越高,即正方形的高度呈升序排列,代码如下:
CPPfor (int i = 1; i <= n; i ++) {
v[a[i].first].push_back(a[i].second);
}
图 处理前后如图 所示:

记 为每个 中最少有多少个正方形; 为每个 中最多有多少个正方形。
图 中,,;图 中,,;
我们开一个大小为 的向量 ,记录每一层正方形的消失时间,
这里 取
1e9+5 即可。看不懂?上代码!
CPPt.resize(maxs + 5); // 开大一点
for (int j = 1; j <= w; j ++) {
for (int i = 0; i < mins; i ++) {
t[i] = max(t[i], v[j][i]);
}
}
for (int i = mins; i < maxs; i ++) {
t[i] = 1e9 + 5;
}
还看不懂?上图片!

预处理完毕,开是处理询问。
对于每个询问,首先注意输入顺序。(编者被坑了!)
接着找到对应的正方形对应的层数 ,一定要使用原来顺序的 数组查找。这可以使用
CPPlower_bound 来优化,返回的是指针,注意减去开头:id = (int)(lower_bound(v[b[A].first].begin(), v[b[A].first].end(), b[A].second) - v[b[A].first].begin());
然后注意到正方形若在 时刻没有正在消失,那么它在 时刻就还存在。
最后,根据预处理的结论,判断 是否小于 即可。如果 ,说明这层没凑齐,正方形自然就不会下落了, 不会超过 ()。
这块内容如果不理解还请看看图 。
代码呈现
代码不丑,请参考。
CPP#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector> // 万能头不是 STL 成员,咱不用。
using namespace std;
pair<int, int> a[200005], b[200005];
vector<int> v[200005], t; // 防止越界,开大数组,从我做起
bool cmp(pair<int, int> x, pair<int, int> y) { // 比较数对
return x.second < y.second;
}
int main() {
int n, w, mins = 1e9+5, maxs = 0, q, A, T, id;
cin >> n >> w;
for (int i = 1; i <= n; i ++) {
cin >> b[i].first >> b[i].second;
a[i] = b[i];
} // 输入
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i ++) {
v[a[i].first].push_back(a[i].second);
} // 离散化??
for (int i = 1; i <= w; i ++) {
mins = min(mins, (int)v[i].size());
maxs = max(maxs, (int)v[i].size());
} // 计算层数
t.resize(maxs + 5);
for (int j = 1; j <= w; j ++) {
for (int i = 0; i < mins; i ++) {
t[i] = max(t[i], v[j][i]);
}
} // 计算每一层的消失时间
for (int i = mins; i < maxs; i ++) {
t[i] = 1e9 + 5;
} // inf
cin >> q;
for (int i = 1; i <= q; i ++) {
cin >> T >> A;
id = (int)(lower_bound(v[b[A].first].begin(), v[b[A].first].end(), b[A].second) - v[b[A].first].begin()); // 二分
cout << (T < t[id] ? "Yes\n" : "No\n"); // 换行!
}
return 0;
}
至此,本题 Accepted。
复杂度分析
预处理除排序外的均摊复杂度是 ,这是因为无论单层还是双层的循环,总的循环次数也不会也不会超过 次。
而每个询问也许会查询到一个有近乎 个正方形的 坐标,由于二分查找的优化,最坏情况下查询的总时间复杂度是 ,最好情况下则是只有 层,最好情况下查询的总时间复杂度就是 。
综合来看,程序的最好时间复杂度是 ,最坏的则是 ,轻松通过!
其实 在此题中发挥了挺大的作用,不然这题的代码就有上千行喽! 好闪,拜谢 !
这篇题解花了我一天的心血,求个赞!
相关推荐
评论
共 29 条评论,欢迎与作者交流。
正在加载评论...