Educational Codeforces Round 132 (C,D) 题解 cf#1709(超详细-解题过程清晰完整
昨晚打了这把EDU,赛后看了dalao们的C题代码豁然开朗恍然大悟
实在是太巧妙了
这场来说,D题的通过率比C题高太多了(估计很多人都在C题卡了然后没做D
AB先跳过
浅讲一下CD题
先放题目链接
题目链接
C - Recover an RBS https://codeforces.ml/contest/1709/problem/C
D - Rorororobot https://codeforces.ml/contest/1709/problem/D
先讲C题吧
C题题意
给你一个括号序,里面部分括号是被问号"?"所取代,问号可以变成左括号或者右括号。
问 使得括号序匹配的做法是否唯一。
题目保证至少存在一种匹配方法。
C题思路
注意题目给到了,保证至少存在一种匹配方法,现在我们把问题转变成了匹配方法是否唯一。
于是我们需要考虑如何找到一种可行的匹配方法。
我们知道 括号序匹配是必须保证所有前缀左括号的个数要大于等于右括号的个数。
并且我们需要拿来取代问号的左括号数量和右括号数量我们也可以直接求得。
(就等于 长度/ 2 - 已知括号数)
由此我们可以发现,如果所有括号先用左括号代替,左括号没了在用右括号代替,这一定是最优的情况。
为什么呢?我们来模拟一下括号序是否匹配的判断,我想不懂的人应该就懂了。
(懂的跳过
我们用now来记录现在前缀中左括号比右括号多了一个
于是当出现左括号时,now++,出现右括号时,now--。
仅当now 小于 0 的时候,我们可以判断该括号序不匹配。
于是,如果我们把左括号尽可能早出现,右括号尽可能晚出现,那是不是这种方案一定是最优方 案。
现在我们知道了最优方案,是左括号先用完,再用右括号,我们暂且称之为Top1.
但是题目问的是存不存在多种方案,这是个值得深思的问题。
显然,我们已经知道了左括号越早出现,那么该方案一定不劣,于是,我们试着把第一个右括号早出现一位(即第一次右括号和最后一个左括号调换位置),这种方案一定是仅次于Top1的方案,暂且称为Top2好了,于是题目就变成了Top2方案是否能使得括号序匹配.
到此为止,题目已经可以做啦
下面展示AC代码
AC代码
#include<iostream>
#include<string>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
using namespace std;
int t = 1, n, m;
string str;
void run() {
int flag = 1;
cin >> str;
int len = str.length();
int cnt = len / 2;
for (int i = 0; i < len; i++)
if (str[i] == '(')
cnt--;
if (cnt == 0) flag = 0;
int now = 0;
for (int i = 0; i < len; i++) {
if (str[i] == '?' && (cnt > 1 || cnt == 0)) {
now++;
cnt--;
} else if (str[i] == '?' && cnt == 1) {
now--;
cnt--;
} else if (str[i] == '?') now--;
if (str[i] == '(') now++;
else if (str[i] == ')') now--;
if (now < 0) flag = 0;
}
if (flag) cout << "NO\n";
else cout << "YES\n";
}
int main () {
io off
cin >> t;
while (t--) {
run();
}
return 0;
}
完结 ★,°:.☆( ̄▽ ̄)/$:.°★ 。
开个玩笑,还有D题。
D题当对于C题来说简单太多了 不知道为什么放在D题位置
可能是因为需要用到数据结构维护,所以就放在了D题吧
D题题意
给一个n行m列的矩阵,第i列的下面a_i个位置被锁住了,无法进入
q次询问,给出起点和终点坐标,和每次连续走几步(连续走的k步必须是直线)
问 是否能在不出界且不经过封锁区域的情况下到达指定位置。
需要注意的是,矩阵的下标是从左下开始的
(n, 1).....(n, m)
. .
. .
. .
. .
(1, 1).....(1, m)
这样应该可以看懂吧!
D题思路
~~很显然~~这是一道维护区间最大值的问题,我想到的是利用线段树维护区间最大值。
找到了两列之间的a_i 的最大值之后,判断他们的横纵坐标分别的差值能不能整除给定的步数。
如果中间有比它们都高的列,那么就要越过中间最高列,需要判断能不能在不出界的情况下越过它。
如果中间最高的列比他们其中一个要低的话,那么只要他们横纵坐标满足条件就一定可以。
AC代码
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
#define int long long
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const double eps = 1e-7;
int n, m, q, maxn[200000 * 20];
int s[200010];
struct s_tree { // 线段树
ll ql, qr, n, p, v;
inline void init(int n) {
for (int i = 1; i <= 200010;i++)
maxn[i] = 0x3f3f3f3f;
memset(s, 0, sizeof(s));
this->n = n;
}
inline ll Query(int o, int L, int R) {
int ans = -0x3f3f3f3f;
int mid = (L + R) >> 1;
if (ql <= L && qr >= R) return maxn[o];
if (mid >= ql) ans = max(ans, Query(o << 1, L, mid));
if (mid < qr) ans = max(ans, Query((o << 1) + 1, mid + 1, R));
return ans;
}
inline ll query(int l, int r) { // 查询
ql = l;qr = r;
return Query(1, 1, n);
}
inline void build(int Case, int L, int R) { // 建树
int mid = (L + R) >> 1;
if (L == R) maxn[Case] = s[L];
else {
build((Case << 1), L, mid); build((Case << 1) + 1, mid + 1, R);
maxn[Case] = max(maxn[Case << 1], maxn[(Case << 1) + 1]);
}
}
} tree;
void run() {
tree.init(n);
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
tree.build(1, 1, n);
cin >> q;
while (q--) {
int x0 = 0, y0 = 0, x2 = 0, y2 = 0, k = 0, flag = 1, maxh = -1;
cin >> x0 >> y0 >> x2 >> y2 >> k;
if (abs(y2 - y0) % k != 0 || abs(x2 - x0) % k != 0) { // 横纵坐标不符合
cout << "No\n";
flag = 0;
}
if (y0 > y2) {
int p = y0;
y0 = y2;
y2 = p;
}
maxh = tree.query(y0, y2); // 找到中间最大值
if (x0 > maxh || x2 > maxh) {
if (flag)
cout << "Yes\n";
flag = 0;
}
int goal = maxh - x0 + 1; // 差值
int res = x0 + (goal / k + (goal % k != 0)) * k; // 越过最高列的目标高度
if (flag) {
if (res <= m) {
cout << "Yes\n";
}
else cout << "No\n";
}
}
}
signed main() {
io off
cin >> m >> n;
run();
return 0;
}
给你一个括号序,里面部分括号是被问号"?"所取代,问号可以变成左括号或者右括号。
问 使得括号序匹配的做法是否唯一。
题目保证至少存在一种匹配方法。
C题思路
注意题目给到了,保证至少存在一种匹配方法,现在我们把问题转变成了匹配方法是否唯一。
于是我们需要考虑如何找到一种可行的匹配方法。
我们知道 括号序匹配是必须保证所有前缀左括号的个数要大于等于右括号的个数。
并且我们需要拿来取代问号的左括号数量和右括号数量我们也可以直接求得。
(就等于 长度/ 2 - 已知括号数)
由此我们可以发现,如果所有括号先用左括号代替,左括号没了在用右括号代替,这一定是最优的情况。
为什么呢?我们来模拟一下括号序是否匹配的判断,我想不懂的人应该就懂了。
(懂的跳过
我们用now来记录现在前缀中左括号比右括号多了一个
于是当出现左括号时,now++,出现右括号时,now--。
仅当now 小于 0 的时候,我们可以判断该括号序不匹配。
于是,如果我们把左括号尽可能早出现,右括号尽可能晚出现,那是不是这种方案一定是最优方 案。
现在我们知道了最优方案,是左括号先用完,再用右括号,我们暂且称之为Top1.
但是题目问的是存不存在多种方案,这是个值得深思的问题。
显然,我们已经知道了左括号越早出现,那么该方案一定不劣,于是,我们试着把第一个右括号早出现一位(即第一次右括号和最后一个左括号调换位置),这种方案一定是仅次于Top1的方案,暂且称为Top2好了,于是题目就变成了Top2方案是否能使得括号序匹配.
到此为止,题目已经可以做啦
下面展示AC代码
AC代码
#include<iostream>
#include<string>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
using namespace std;
int t = 1, n, m;
string str;
void run() {
int flag = 1;
cin >> str;
int len = str.length();
int cnt = len / 2;
for (int i = 0; i < len; i++)
if (str[i] == '(')
cnt--;
if (cnt == 0) flag = 0;
int now = 0;
for (int i = 0; i < len; i++) {
if (str[i] == '?' && (cnt > 1 || cnt == 0)) {
now++;
cnt--;
} else if (str[i] == '?' && cnt == 1) {
now--;
cnt--;
} else if (str[i] == '?') now--;
if (str[i] == '(') now++;
else if (str[i] == ')') now--;
if (now < 0) flag = 0;
}
if (flag) cout << "NO\n";
else cout << "YES\n";
}
int main () {
io off
cin >> t;
while (t--) {
run();
}
return 0;
}
完结 ★,°:.☆( ̄▽ ̄)/$:.°★ 。
开个玩笑,还有D题。
D题当对于C题来说简单太多了 不知道为什么放在D题位置
可能是因为需要用到数据结构维护,所以就放在了D题吧
D题题意
给一个n行m列的矩阵,第i列的下面a_i个位置被锁住了,无法进入
q次询问,给出起点和终点坐标,和每次连续走几步(连续走的k步必须是直线)
问 是否能在不出界且不经过封锁区域的情况下到达指定位置。
需要注意的是,矩阵的下标是从左下开始的
(n, 1).....(n, m)
. .
. .
. .
. .
(1, 1).....(1, m)
这样应该可以看懂吧!
D题思路
~~很显然~~这是一道维护区间最大值的问题,我想到的是利用线段树维护区间最大值。
找到了两列之间的a_i 的最大值之后,判断他们的横纵坐标分别的差值能不能整除给定的步数。
如果中间有比它们都高的列,那么就要越过中间最高列,需要判断能不能在不出界的情况下越过它。
如果中间最高的列比他们其中一个要低的话,那么只要他们横纵坐标满足条件就一定可以。
AC代码
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
#define int long long
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const double eps = 1e-7;
int n, m, q, maxn[200000 * 20];
int s[200010];
struct s_tree { // 线段树
ll ql, qr, n, p, v;
inline void init(int n) {
for (int i = 1; i <= 200010;i++)
maxn[i] = 0x3f3f3f3f;
memset(s, 0, sizeof(s));
this->n = n;
}
inline ll Query(int o, int L, int R) {
int ans = -0x3f3f3f3f;
int mid = (L + R) >> 1;
if (ql <= L && qr >= R) return maxn[o];
if (mid >= ql) ans = max(ans, Query(o << 1, L, mid));
if (mid < qr) ans = max(ans, Query((o << 1) + 1, mid + 1, R));
return ans;
}
inline ll query(int l, int r) { // 查询
ql = l;qr = r;
return Query(1, 1, n);
}
inline void build(int Case, int L, int R) { // 建树
int mid = (L + R) >> 1;
if (L == R) maxn[Case] = s[L];
else {
build((Case << 1), L, mid); build((Case << 1) + 1, mid + 1, R);
maxn[Case] = max(maxn[Case << 1], maxn[(Case << 1) + 1]);
}
}
} tree;
void run() {
tree.init(n);
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
tree.build(1, 1, n);
cin >> q;
while (q--) {
int x0 = 0, y0 = 0, x2 = 0, y2 = 0, k = 0, flag = 1, maxh = -1;
cin >> x0 >> y0 >> x2 >> y2 >> k;
if (abs(y2 - y0) % k != 0 || abs(x2 - x0) % k != 0) { // 横纵坐标不符合
cout << "No\n";
flag = 0;
}
if (y0 > y2) {
int p = y0;
y0 = y2;
y2 = p;
}
maxh = tree.query(y0, y2); // 找到中间最大值
if (x0 > maxh || x2 > maxh) {
if (flag)
cout << "Yes\n";
flag = 0;
}
int goal = maxh - x0 + 1; // 差值
int res = x0 + (goal / k + (goal % k != 0)) * k; // 越过最高列的目标高度
if (flag) {
if (res <= m) {
cout << "Yes\n";
}
else cout << "No\n";
}
}
}
signed main() {
io off
cin >> m >> n;
run();
return 0;
}
注意题目给到了,保证至少存在一种匹配方法,现在我们把问题转变成了匹配方法是否唯一。
于是我们需要考虑如何找到一种可行的匹配方法。
我们知道 括号序匹配是必须保证所有前缀左括号的个数要大于等于右括号的个数。
并且我们需要拿来取代问号的左括号数量和右括号数量我们也可以直接求得。
(就等于 长度/ 2 - 已知括号数)
由此我们可以发现,如果所有括号先用左括号代替,左括号没了在用右括号代替,这一定是最优的情况。
为什么呢?我们来模拟一下括号序是否匹配的判断,我想不懂的人应该就懂了。
(懂的跳过
我们用now来记录现在前缀中左括号比右括号多了一个
于是当出现左括号时,now++,出现右括号时,now--。
仅当now 小于 0 的时候,我们可以判断该括号序不匹配。
于是,如果我们把左括号尽可能早出现,右括号尽可能晚出现,那是不是这种方案一定是最优方 案。
现在我们知道了最优方案,是左括号先用完,再用右括号,我们暂且称之为Top1.
但是题目问的是存不存在多种方案,这是个值得深思的问题。
显然,我们已经知道了左括号越早出现,那么该方案一定不劣,于是,我们试着把第一个右括号早出现一位(即第一次右括号和最后一个左括号调换位置),这种方案一定是仅次于Top1的方案,暂且称为Top2好了,于是题目就变成了Top2方案是否能使得括号序匹配.
到此为止,题目已经可以做啦
下面展示AC代码
AC代码
#include<iostream>
#include<string>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
using namespace std;
int t = 1, n, m;
string str;
void run() {
int flag = 1;
cin >> str;
int len = str.length();
int cnt = len / 2;
for (int i = 0; i < len; i++)
if (str[i] == '(')
cnt--;
if (cnt == 0) flag = 0;
int now = 0;
for (int i = 0; i < len; i++) {
if (str[i] == '?' && (cnt > 1 || cnt == 0)) {
now++;
cnt--;
} else if (str[i] == '?' && cnt == 1) {
now--;
cnt--;
} else if (str[i] == '?') now--;
if (str[i] == '(') now++;
else if (str[i] == ')') now--;
if (now < 0) flag = 0;
}
if (flag) cout << "NO\n";
else cout << "YES\n";
}
int main () {
io off
cin >> t;
while (t--) {
run();
}
return 0;
}
完结 ★,°:.☆( ̄▽ ̄)/$:.°★ 。
开个玩笑,还有D题。
D题当对于C题来说简单太多了 不知道为什么放在D题位置
可能是因为需要用到数据结构维护,所以就放在了D题吧
D题题意
给一个n行m列的矩阵,第i列的下面a_i个位置被锁住了,无法进入
q次询问,给出起点和终点坐标,和每次连续走几步(连续走的k步必须是直线)
问 是否能在不出界且不经过封锁区域的情况下到达指定位置。
需要注意的是,矩阵的下标是从左下开始的
(n, 1).....(n, m)
. .
. .
. .
. .
(1, 1).....(1, m)
这样应该可以看懂吧!
D题思路
~~很显然~~这是一道维护区间最大值的问题,我想到的是利用线段树维护区间最大值。
找到了两列之间的a_i 的最大值之后,判断他们的横纵坐标分别的差值能不能整除给定的步数。
如果中间有比它们都高的列,那么就要越过中间最高列,需要判断能不能在不出界的情况下越过它。
如果中间最高的列比他们其中一个要低的话,那么只要他们横纵坐标满足条件就一定可以。
AC代码
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
#define int long long
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const double eps = 1e-7;
int n, m, q, maxn[200000 * 20];
int s[200010];
struct s_tree { // 线段树
ll ql, qr, n, p, v;
inline void init(int n) {
for (int i = 1; i <= 200010;i++)
maxn[i] = 0x3f3f3f3f;
memset(s, 0, sizeof(s));
this->n = n;
}
inline ll Query(int o, int L, int R) {
int ans = -0x3f3f3f3f;
int mid = (L + R) >> 1;
if (ql <= L && qr >= R) return maxn[o];
if (mid >= ql) ans = max(ans, Query(o << 1, L, mid));
if (mid < qr) ans = max(ans, Query((o << 1) + 1, mid + 1, R));
return ans;
}
inline ll query(int l, int r) { // 查询
ql = l;qr = r;
return Query(1, 1, n);
}
inline void build(int Case, int L, int R) { // 建树
int mid = (L + R) >> 1;
if (L == R) maxn[Case] = s[L];
else {
build((Case << 1), L, mid); build((Case << 1) + 1, mid + 1, R);
maxn[Case] = max(maxn[Case << 1], maxn[(Case << 1) + 1]);
}
}
} tree;
void run() {
tree.init(n);
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
tree.build(1, 1, n);
cin >> q;
while (q--) {
int x0 = 0, y0 = 0, x2 = 0, y2 = 0, k = 0, flag = 1, maxh = -1;
cin >> x0 >> y0 >> x2 >> y2 >> k;
if (abs(y2 - y0) % k != 0 || abs(x2 - x0) % k != 0) { // 横纵坐标不符合
cout << "No\n";
flag = 0;
}
if (y0 > y2) {
int p = y0;
y0 = y2;
y2 = p;
}
maxh = tree.query(y0, y2); // 找到中间最大值
if (x0 > maxh || x2 > maxh) {
if (flag)
cout << "Yes\n";
flag = 0;
}
int goal = maxh - x0 + 1; // 差值
int res = x0 + (goal / k + (goal % k != 0)) * k; // 越过最高列的目标高度
if (flag) {
if (res <= m) {
cout << "Yes\n";
}
else cout << "No\n";
}
}
}
signed main() {
io off
cin >> m >> n;
run();
return 0;
}
#include<iostream>
#include<string>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
using namespace std;
int t = 1, n, m;
string str;
void run() {
int flag = 1;
cin >> str;
int len = str.length();
int cnt = len / 2;
for (int i = 0; i < len; i++)
if (str[i] == '(')
cnt--;
if (cnt == 0) flag = 0;
int now = 0;
for (int i = 0; i < len; i++) {
if (str[i] == '?' && (cnt > 1 || cnt == 0)) {
now++;
cnt--;
} else if (str[i] == '?' && cnt == 1) {
now--;
cnt--;
} else if (str[i] == '?') now--;
if (str[i] == '(') now++;
else if (str[i] == ')') now--;
if (now < 0) flag = 0;
}
if (flag) cout << "NO\n";
else cout << "YES\n";
}
int main () {
io off
cin >> t;
while (t--) {
run();
}
return 0;
}
D题当对于C题来说简单太多了
可能是因为需要用到数据结构维护,所以就放在了D题吧
D题题意
给一个n行m列的矩阵,第i列的下面a_i个位置被锁住了,无法进入
q次询问,给出起点和终点坐标,和每次连续走几步(连续走的k步必须是直线)
问 是否能在不出界且不经过封锁区域的情况下到达指定位置。
需要注意的是,矩阵的下标是从左下开始的
(n, 1).....(n, m)
. .
. .
. .
. .
(1, 1).....(1, m)
这样应该可以看懂吧!
D题思路
~~很显然~~这是一道维护区间最大值的问题,我想到的是利用线段树维护区间最大值。
找到了两列之间的a_i 的最大值之后,判断他们的横纵坐标分别的差值能不能整除给定的步数。
如果中间有比它们都高的列,那么就要越过中间最高列,需要判断能不能在不出界的情况下越过它。
如果中间最高的列比他们其中一个要低的话,那么只要他们横纵坐标满足条件就一定可以。
AC代码
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
#define int long long
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const double eps = 1e-7;
int n, m, q, maxn[200000 * 20];
int s[200010];
struct s_tree { // 线段树
ll ql, qr, n, p, v;
inline void init(int n) {
for (int i = 1; i <= 200010;i++)
maxn[i] = 0x3f3f3f3f;
memset(s, 0, sizeof(s));
this->n = n;
}
inline ll Query(int o, int L, int R) {
int ans = -0x3f3f3f3f;
int mid = (L + R) >> 1;
if (ql <= L && qr >= R) return maxn[o];
if (mid >= ql) ans = max(ans, Query(o << 1, L, mid));
if (mid < qr) ans = max(ans, Query((o << 1) + 1, mid + 1, R));
return ans;
}
inline ll query(int l, int r) { // 查询
ql = l;qr = r;
return Query(1, 1, n);
}
inline void build(int Case, int L, int R) { // 建树
int mid = (L + R) >> 1;
if (L == R) maxn[Case] = s[L];
else {
build((Case << 1), L, mid); build((Case << 1) + 1, mid + 1, R);
maxn[Case] = max(maxn[Case << 1], maxn[(Case << 1) + 1]);
}
}
} tree;
void run() {
tree.init(n);
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
tree.build(1, 1, n);
cin >> q;
while (q--) {
int x0 = 0, y0 = 0, x2 = 0, y2 = 0, k = 0, flag = 1, maxh = -1;
cin >> x0 >> y0 >> x2 >> y2 >> k;
if (abs(y2 - y0) % k != 0 || abs(x2 - x0) % k != 0) { // 横纵坐标不符合
cout << "No\n";
flag = 0;
}
if (y0 > y2) {
int p = y0;
y0 = y2;
y2 = p;
}
maxh = tree.query(y0, y2); // 找到中间最大值
if (x0 > maxh || x2 > maxh) {
if (flag)
cout << "Yes\n";
flag = 0;
}
int goal = maxh - x0 + 1; // 差值
int res = x0 + (goal / k + (goal % k != 0)) * k; // 越过最高列的目标高度
if (flag) {
if (res <= m) {
cout << "Yes\n";
}
else cout << "No\n";
}
}
}
signed main() {
io off
cin >> m >> n;
run();
return 0;
}
给一个n行m列的矩阵,第i列的下面a_i个位置被锁住了,无法进入
q次询问,给出起点和终点坐标,和每次连续走几步(连续走的k步必须是直线)
问 是否能在不出界且不经过封锁区域的情况下到达指定位置。
需要注意的是,矩阵的下标是从左下开始的
(n, 1).....(n, m)
. .
. .
. .
. .
(1, 1).....(1, m)
这样应该可以看懂吧!
D题思路
~~很显然~~这是一道维护区间最大值的问题,我想到的是利用线段树维护区间最大值。
找到了两列之间的a_i 的最大值之后,判断他们的横纵坐标分别的差值能不能整除给定的步数。
如果中间有比它们都高的列,那么就要越过中间最高列,需要判断能不能在不出界的情况下越过它。
如果中间最高的列比他们其中一个要低的话,那么只要他们横纵坐标满足条件就一定可以。
AC代码
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
#define int long long
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const double eps = 1e-7;
int n, m, q, maxn[200000 * 20];
int s[200010];
struct s_tree { // 线段树
ll ql, qr, n, p, v;
inline void init(int n) {
for (int i = 1; i <= 200010;i++)
maxn[i] = 0x3f3f3f3f;
memset(s, 0, sizeof(s));
this->n = n;
}
inline ll Query(int o, int L, int R) {
int ans = -0x3f3f3f3f;
int mid = (L + R) >> 1;
if (ql <= L && qr >= R) return maxn[o];
if (mid >= ql) ans = max(ans, Query(o << 1, L, mid));
if (mid < qr) ans = max(ans, Query((o << 1) + 1, mid + 1, R));
return ans;
}
inline ll query(int l, int r) { // 查询
ql = l;qr = r;
return Query(1, 1, n);
}
inline void build(int Case, int L, int R) { // 建树
int mid = (L + R) >> 1;
if (L == R) maxn[Case] = s[L];
else {
build((Case << 1), L, mid); build((Case << 1) + 1, mid + 1, R);
maxn[Case] = max(maxn[Case << 1], maxn[(Case << 1) + 1]);
}
}
} tree;
void run() {
tree.init(n);
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
tree.build(1, 1, n);
cin >> q;
while (q--) {
int x0 = 0, y0 = 0, x2 = 0, y2 = 0, k = 0, flag = 1, maxh = -1;
cin >> x0 >> y0 >> x2 >> y2 >> k;
if (abs(y2 - y0) % k != 0 || abs(x2 - x0) % k != 0) { // 横纵坐标不符合
cout << "No\n";
flag = 0;
}
if (y0 > y2) {
int p = y0;
y0 = y2;
y2 = p;
}
maxh = tree.query(y0, y2); // 找到中间最大值
if (x0 > maxh || x2 > maxh) {
if (flag)
cout << "Yes\n";
flag = 0;
}
int goal = maxh - x0 + 1; // 差值
int res = x0 + (goal / k + (goal % k != 0)) * k; // 越过最高列的目标高度
if (flag) {
if (res <= m) {
cout << "Yes\n";
}
else cout << "No\n";
}
}
}
signed main() {
io off
cin >> m >> n;
run();
return 0;
}
~~很显然~~这是一道维护区间最大值的问题,我想到的是利用线段树维护区间最大值。
找到了两列之间的a_i 的最大值之后,判断他们的横纵坐标分别的差值能不能整除给定的步数。
如果中间有比它们都高的列,那么就要越过中间最高列,需要判断能不能在不出界的情况下越过它。
如果中间最高的列比他们其中一个要低的话,那么只要他们横纵坐标满足条件就一定可以。
AC代码
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
#define int long long
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const double eps = 1e-7;
int n, m, q, maxn[200000 * 20];
int s[200010];
struct s_tree { // 线段树
ll ql, qr, n, p, v;
inline void init(int n) {
for (int i = 1; i <= 200010;i++)
maxn[i] = 0x3f3f3f3f;
memset(s, 0, sizeof(s));
this->n = n;
}
inline ll Query(int o, int L, int R) {
int ans = -0x3f3f3f3f;
int mid = (L + R) >> 1;
if (ql <= L && qr >= R) return maxn[o];
if (mid >= ql) ans = max(ans, Query(o << 1, L, mid));
if (mid < qr) ans = max(ans, Query((o << 1) + 1, mid + 1, R));
return ans;
}
inline ll query(int l, int r) { // 查询
ql = l;qr = r;
return Query(1, 1, n);
}
inline void build(int Case, int L, int R) { // 建树
int mid = (L + R) >> 1;
if (L == R) maxn[Case] = s[L];
else {
build((Case << 1), L, mid); build((Case << 1) + 1, mid + 1, R);
maxn[Case] = max(maxn[Case << 1], maxn[(Case << 1) + 1]);
}
}
} tree;
void run() {
tree.init(n);
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
tree.build(1, 1, n);
cin >> q;
while (q--) {
int x0 = 0, y0 = 0, x2 = 0, y2 = 0, k = 0, flag = 1, maxh = -1;
cin >> x0 >> y0 >> x2 >> y2 >> k;
if (abs(y2 - y0) % k != 0 || abs(x2 - x0) % k != 0) { // 横纵坐标不符合
cout << "No\n";
flag = 0;
}
if (y0 > y2) {
int p = y0;
y0 = y2;
y2 = p;
}
maxh = tree.query(y0, y2); // 找到中间最大值
if (x0 > maxh || x2 > maxh) {
if (flag)
cout << "Yes\n";
flag = 0;
}
int goal = maxh - x0 + 1; // 差值
int res = x0 + (goal / k + (goal % k != 0)) * k; // 越过最高列的目标高度
if (flag) {
if (res <= m) {
cout << "Yes\n";
}
else cout << "No\n";
}
}
}
signed main() {
io off
cin >> m >> n;
run();
return 0;
}
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false);
#define off cin.tie(0), cout.tie(0);
#define int long long
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const double eps = 1e-7;
int n, m, q, maxn[200000 * 20];
int s[200010];
struct s_tree { // 线段树
ll ql, qr, n, p, v;
inline void init(int n) {
for (int i = 1; i <= 200010;i++)
maxn[i] = 0x3f3f3f3f;
memset(s, 0, sizeof(s));
this->n = n;
}
inline ll Query(int o, int L, int R) {
int ans = -0x3f3f3f3f;
int mid = (L + R) >> 1;
if (ql <= L && qr >= R) return maxn[o];
if (mid >= ql) ans = max(ans, Query(o << 1, L, mid));
if (mid < qr) ans = max(ans, Query((o << 1) + 1, mid + 1, R));
return ans;
}
inline ll query(int l, int r) { // 查询
ql = l;qr = r;
return Query(1, 1, n);
}
inline void build(int Case, int L, int R) { // 建树
int mid = (L + R) >> 1;
if (L == R) maxn[Case] = s[L];
else {
build((Case << 1), L, mid); build((Case << 1) + 1, mid + 1, R);
maxn[Case] = max(maxn[Case << 1], maxn[(Case << 1) + 1]);
}
}
} tree;
void run() {
tree.init(n);
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
tree.build(1, 1, n);
cin >> q;
while (q--) {
int x0 = 0, y0 = 0, x2 = 0, y2 = 0, k = 0, flag = 1, maxh = -1;
cin >> x0 >> y0 >> x2 >> y2 >> k;
if (abs(y2 - y0) % k != 0 || abs(x2 - x0) % k != 0) { // 横纵坐标不符合
cout << "No\n";
flag = 0;
}
if (y0 > y2) {
int p = y0;
y0 = y2;
y2 = p;
}
maxh = tree.query(y0, y2); // 找到中间最大值
if (x0 > maxh || x2 > maxh) {
if (flag)
cout << "Yes\n";
flag = 0;
}
int goal = maxh - x0 + 1; // 差值
int res = x0 + (goal / k + (goal % k != 0)) * k; // 越过最高列的目标高度
if (flag) {
if (res <= m) {
cout << "Yes\n";
}
else cout << "No\n";
}
}
}
signed main() {
io off
cin >> m >> n;
run();
return 0;
}

浙公网安备 33010602011771号