Educational Codeforces Round 132 (Rated for Div. 2) A-D
Educational Codeforces Round 132 (Rated for Div. 2)
https://codeforces.com/contest/1709
这场ab很模拟(但我写的很慢),c居然比d难(策略性失误,悲)
A. Three Doors
读题读了半天
题意
有三扇门,编号1-3,只有对应的钥匙才能开这扇门。
初始的时候手里有一把钥匙
现在有两扇门后面各藏着一把钥匙,打开这扇门之后就能拿到这个钥匙。
问现有的条件下能否打开所有的门
分析
先拿手中的钥匙去开对应的门,开了之后门后钥匙对应的门也能被打开,对应的一一标记即可
(我写的比较繁琐。。。)
Code
#include <bits/stdc++.h>
using namespace std;
bool vis[4];
void solve () {
memset (vis, false, sizeof vis);
int x, a, b, c;
cin >> x >> a >> b >> c;
if (x == 1) {
vis[1] = true;
vis[a] = true;
if (a == 3) vis[c] = true;
else if (a == 2) vis[b] = true;
}
else if (x == 2) {
vis[2] = true;
vis[b] = true;
if (b == 3) vis[c] = true;
else if (b == 1) vis[a] = true;
}
else {
vis[3] = true;
vis[c] = true;
if (c == 1) vis[a] = true;
else if (c == 2) vis[b] = true;
}
if (vis[3] && vis[1] && vis[2]) cout << "YES\n";
else cout << "NO\n";
}
int main () {
int t;
cin >> t;
while (t --) solve ();
}
B. Also Try Minecraft
题意
给定序列a,长度为n, 从 \(a_i\) 到 \(a_j\) 有一个伤害值:如果 \(a_i < a_j\),则值为0;否则为 \(a_i-a_j\)。每次只能向相邻的转移,即只能从 \(a_i\) 到 \(a_{i+1}\),或到 \(a_{i-1}\),(\(1<i<n\))。有q次询问,问每次从 \(s_j\) 到 \(t_j\) 的最小伤害值是多少
分析
要么向前走要么向后走一格,然后伤害值又是非负数,那么走出去再倒回来的伤害值一定比直接走过去要大,所以没有策略而言,直接统计差值的前缀和即可。
正着一遍,倒着一遍
记得开long long !!!
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5;
int a[N], b[N], c[N], n;
void test () {
for (int i = 1; i <= n; i ++) cout << b[i] << ' ';
cout << endl;
for (int i = 1; i <= n; i ++) cout << c[i] << ' ';
cout << endl << endl;
}
void solve () {
int m;
cin >> n >> m;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 2; i <= n; i ++) b[i] = max (0ll, a[i-1] - a[i]);
for (int i = n-1; i >= 1; i --) c[i] = max(a[i+1] - a[i], 0ll);
//test();
for (int i = 2; i <= n; i ++) b[i] += b[i-1];
for (int i = n-1; i >= 1; i --) c[i] += c[i+1];
//test();
while (m --) {
int s, t;
cin >> s >> t;
if (s < t) cout << b[t] - b[s] << endl;
else cout << c[t] - c[s] << endl;
}
}
signed main () {
solve ();
}
//s到t,每次可以前进或后退一格
//i->j:
//a[i]<a[j]:0
//a[i]>a[j]:a[i]-a[j]
//max(a[i]-a[j], 0)
//最小值
//答案是固定的,不会回退
//预处理
C. Recover an RBS
WA了5发并且是赛后过的(悲
题意
给一个字符串,包含 '(' ')' '?'
\(RBS\) 就是能够两两匹配的括号序列,'?'出表示还未确定。
问能否构造出 唯一 的括号序列
(题目保证一定存在一个括号序列)
分析
(n为字符串长度)
把 '(' 视为0, ')' 视为1,那么要形成 RBS 的条件就是:
\(1-n\) 任意前缀满足 \(cnt0\geq cnt1\),且最终
\(cnt1=cnt0=\frac n2\)
明确了这一点,就可以来构造序列了,必定存在的一种合法构造方式,就是尽可能把 '(' 放在前面,即在 \(cnt0<\frac n2\)的前提下都放'('。这样就必定构造出一种合法的 RBS 了。
然后在前面构造的过程中,记录?变为'('的位置,保存在v1, 记录?变为')'的位置,保存在v2。
然后枚举每一个'('和')',交换他俩的位置,交换之后check一下是否合法, 如果能找到一个合法的并且与最开始构造的 不一样的串,那么就表示不唯一。
否则,所有的都交换完了但还找到不一样的,就表示只有唯一的RBS
Code
#include <bits/stdc++.h>
using namespace std;
//是否合法
bool check (string s) {
int n = s.size ();
int cnt0 = 0, cnt1 = 0;
for (int i = 0; i < n; i ++) {
if (s[i] == '(') cnt0 ++;
else cnt1 ++;
if (cnt1 > cnt0) return false;
}
if (cnt1 != cnt0) return false;
return true;
}
void solve () {
string s;
cin >> s;
int n = s.size (), cnt = 0;
int cnt1 = 0, cnt0 = 0;
vector <int> v; //记录?的位置
for (int i = 0; i < n; i ++) {
if (s[i] == '?') {
if (i == 0) s[0] = '(', cnt0 ++;
else if (i == n-1) s[n-1] = ')', cnt1 ++;
else cnt ++;
}
else if (s[i] == '(') cnt0 ++;
else if (s[i] == ')') cnt1 ++;
}
if (cnt < 2 || cnt0 == n/2 || cnt1 == n/2) {
cout << "YES\n";
return ;
}
string t1 = s;
int st = n, ed = 0, res = n/2 - cnt0; //res表示最多还能放多少个'('
cnt1 = cnt0 = 0;
//cout << res << endl;
vector <int> v1, v2;
for (int i = 0; i < n; i ++) {
if (s[i] == '(') cnt0 ++;
else if (s[i] == ')') cnt1 ++;
else {
if (cnt0 == cnt1) {
cnt0 ++, t1[i] = '(';
v1.push_back (i), res --;//必为'('
}
else {
if (res) {
cnt0 ++, t1[i] = '(';
v1.push_back (i), res --;//必为'('
}
else {
cnt1 ++, t1[i] = ')';
v2.push_back (i);//必为'('
}
}
}
}
//cout << t1 << endl;
for (auto i: v1)
for (auto j: v2) {
string tmp = t1;
swap (tmp[i], tmp[j]);
if (check (tmp) && t1 != tmp) {
cout << "NO\n";
return ;
}
}
cout << "YES\n";
}
int main () {
int t;
cin >> t;
while (t --) solve ();
}
//求 可匹配的括号序列是否唯一
//保证一定有合法序列
//n一定为偶数
//首尾固定
//'(': cnt0++;
//')': cnt1++;
//cnt0>=cnt1,且最终cnt1==cnt0
D. Rorororobot
题意
\(n*m\) 的方格,某些位置(\(1-a_m\))被block了
可以上下左右走但是只能一次走k个,且不能出界
q次询问,给定\((sx,sy),(fx,fy),k\), 问能不能从 s 走到 f 呢
统计曼哈顿距离
读入量大,不要用cin!!!!
分析
先判断横坐标之差和纵坐标之差是否都是k的倍数,不是一定走不到
对于每一列,最远能够走到:
故只需查询区间 \([l,r]\) 的最大 \(a_i\) 即可(看有无被挡住
查询可用线段树和ST表
(早该学学ST表了,下午出个ST表的笔寄吧)
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int a[N], f[N][25], n, m; //st表
void init () {
for (int j = 1; j < 23; j ++)
for (int i = 1; i + (1 << j) - 1 <= m; i++) //m!!!
f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]); // ST表具体实现
}
int query (int l, int r) {
int k = __lg (r - l + 1);
return max (f[l][k], f[r-(1<<k)+1][k]);
}
int main () {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i ++) scanf("%d", &f[i][0]);
init ();
int q;
cin >> q;
while (q --) {
int sx, sy, fx, fy, k;
scanf("%d%d%d%d%d", &sx, &sy, &fx, &fy, &k);
int dx = abs (fx - sx), dy = abs (fy - sy);
if ((dx % k) || (dy % k)) {
cout << "NO\n";
continue;
}
bool suc = true;
int maxn = sx + (n - sx)/k * k, l, r;
if (sy < fy) l = sy + 1, r = fy;
else if (sy > fy) l = fy, r = sy - 1;
if (sy != fy && maxn <= query (l, r)) suc = false;
if (suc) cout<<"YES"<<"\n";
else cout << "NO\n";
}
}
//n*m的方格,某些位置(1-am)被block
//可以上下左右走但是只能一次走k个
//q次询问,(sx,sy),(fx,fy),k 问能不能从s走到f呢
//统计曼哈顿距离