CF777题解

发布时间 2023-10-27 15:09:52作者: Kazdale
  • 分析

    先对每一列都做 DP 寻找极长单调不降区间,能够得到若干极长单调不降区间,只要询问的区间是这些区间的子区间,那么说明在这个区间内必有一列的这个区间是单调不降的。

    思考如何快速判断子区间。
    \(f_{x}\) 表示以 \(x\) 为所有左端点为 \(x\) 的区间的右端点最大值,那么对于询问区间 \([L,R]\),我们只需要判 \(R \leq f_{L}\) 就可以证明合法,反之则不合法。

    思考如何求 \(f_{x}\) 数组。
    首先 \(f_{x}\) 一定等于所有左端点为 \(x\) 的极长区间的右端点最大值,同时对于左端点小于 \(x\) 的所有极长区间,其右端点的最大值也可以传递给 \(x\)
    那么我们先求一遍 \(f_x\) 表示所有左端点为 \(x\) 的极长区间的右端点最大值,然后再求一遍前缀最大值表示所有左端点为 \(x\) 的区间的右端点最大值。

    值得注意的是,因为这个数组的二维可能有一维过大,所以要先用一维数组存储,然后再按下标对 \(m\) 取模的模数分类存到 vector 里就可以当成二维数组做了。

  • 代码

#include <iostream>
#include <vector>
using namespace std;
constexpr int MAXN(1000007);
vector <int> e[MAXN];
int r[MAXN], a[MAXN];
int n, m, q;
inline void read(int &temp) { cin >> temp; }
int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	read(n), read(m);
	for (int i(1); i <= n * m; ++i)  read(a[i]);
	for (int j(0); j < m; ++j)  e[j].push_back(0);
	for (int i(1); i <= n * m; ++i)  e[i % m].push_back(a[i]);
	for (int j(0); j < m; ++j) {
		int lst(1);
		for (int i(1); i <= n; ++i)  if (e[j][i] < e[j][i - 1])  r[lst] = max(r[lst], i - 1), lst = i;
		r[lst] = max(r[lst], (int)e[j].size() - 1);
	}
	for (int i(1); i <= n; ++i)  r[i] = max(i, max(r[i - 1], r[i]));
	read(q);
	for (int i(1), L, R; i <= q; ++i) {
		read(L), read(R);
		if (R > r[L])  cout << "No" << endl;
		else  cout << "Yes" << endl;
	}
	return 0;
}