AtCoder Beginner Contest 223(补题)
Posted 佐鼬Jun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Beginner Contest 223(补题)相关的知识,希望对你有一定的参考价值。
F - Parenthesis Checking
链接: link.
题意:
给定一个括号字符串,现在有两种操作
操作1:给定l,r,交换l,r的字符
操作2: 给定l,r,查询这个区间的括号是否合法
思路:
( 定义为+1,) 定义为-1,对于原串,维护一个前缀和
对于查询操作 [l,r] 区间合法的话,这个区间的和必须是0,代表整体匹配,区间的前缀和中的每个值都必须
>
=
0
>=0
>=0,代表内部每个括号的匹配了。
简化下来就是,区间内前缀和的最小值必须>=0,区间和必须=0
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
string s;
int a[N];
int sum[N];
struct node {
int l, r;
int minv;
int add;
} tr[N * 4];
int n, m;
void pushup(int u) { tr[u].minv = min(tr[u << 1].minv, tr[u << 1 | 1].minv); }
void pushdown(int u) {
if (tr[u].add) {
tr[u << 1].add += tr[u].add;
tr[u << 1 | 1].add += tr[u].add;
tr[u << 1].minv += tr[u].add;
tr[u << 1 | 1].minv += tr[u].add;
tr[u].add = 0;
}
}
void build(int u, int l, int r) {
if (l == r) {
tr[u] = {l, r};
return;
}
tr[u].l = l, tr[u].r = r;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int x) {
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].minv += x;
tr[u].add += x;
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, x);
if (r > mid) modify(u << 1 | 1, l, r, x);
pushup(u);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].minv;
}
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
int res = 1e9;
if (l <= mid) res = min(res, query(u << 1, l, r));
if (r > mid) res = min(res, query(u << 1 | 1, l, r));
return res;
}
int main() {
cin >> n >> m;
cin >> s;
int len = s.size();
for (int i = 0; i < n; i++) {
if (s[i] == '(')
a[i + 1] = 1;
else
a[i + 1] = -1;
}
build(1, 1, n);
for (int i = 1; i <= n; i++) {
modify(1, i, n, a[i]);
}
while (m--) {
int op, l, r;
cin >> op >> l >> r;
if (op == 1) {
modify(1, l, n, -a[l]);
modify(1, r, n, -a[r]);
swap(a[l], a[r]);
modify(1, l, n, a[l]);
modify(1, r, n, a[r]);
} else {
int pre;
if (l == 1)
pre = 0;
else
pre = query(1, l - 1, l - 1);
if (query(1, l, r) - pre == 0 && query(1, r, r) - pre == 0) {
puts("Yes");
} else
puts("No");
}
}
}
E - Placing Rectangles
链接: link.
题意:
给定一个正整数 X X X和 Y Y Y,在二维坐标轴第一象限,代表给定给定一个长为 X X X,宽为 Y Y Y的矩形,现在让你往这个矩阵里放三个小矩形,三个小矩形的横坐标不能超过 X X X,纵坐标不能超过 Y Y Y,三个小矩形的面积不能小于A,B,C
思路:
先讨论对于大矩形中放,放两个小矩形,如果合法,一定存在一条平行于 x x x轴(或者 y y y轴)的线,把两个小矩形分开,如果不存在,说明两个小矩形一定有重合。下面拿平行宇x轴距离,此时只需要先放一个矩形,然后利用 y = S x y=\\frac{S}{x} y=xS(向上取整),因为题意要求小矩形坐标必须是整数,然后剩下的长度就是 Y − y Y-y Y−y,看剩下的面积能否放的下,一个小矩形。
对于放三个小矩形,如果合法一定存在一条平行于 x x x轴(或者 y y y轴)的直线,把三个矩形分开,一边是两个矩形,一边是一个矩形。先枚举 x x x或者 y y y,然后再把一个矩形定下放在一边,利用 y = S x y=\\frac{S}{x} y=xS(向上取整),剩下的长度就是 Y − y Y-y Y−y,这是直线另一边的长度,对于下面的矩形利用上面的方法枚举即可。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
bool solve(ll x, ll y, ll a, ll b) {
for (int i = 0; i < 2; i++) {
ll len = (a + x - 1) / x;
if (len < y && x * (y - len) >= b) {
return true;
}
swap(x, y);
}
return false;
}
bool check(ll x, ll y, ll a, ll b, ll c) {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
ll len = (a + x - 1) / x;
if (len < y && solve(x, y - len, b, c)) {
return true;
}
swap(a, b);
swap(b, c);
}
swap(x, y);
}
return false;
}
int main() {
ll x, y, a, b, c;
cin >> x >> y >> a >> b >> c;
bool t = check(x, y, a, b, c);
if (t)
puts("Yes");
else
puts("No");
return 0;
}
D - Restricted Permutation
链接: link.
题意:
现在序列 P P P是{1,2,3…n}的排列,现在给定 m m m个条件,即 A i A_i Ai在 B i B_i Bi前面,让你输出字典序最小的排列。
思路:
A i A_i Ai比 B i B_i Bi小,就把两个数当成图论中的两个点,从 A i A_i Ai往 B i B_i Bi连一条边,然后跑一边拓扑排序,并用优先队列维护最小值,把出队列顺序记下来即可
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int h[N], e[N], ne[N], idx;
int du[N];
int n, m;
vector<int> path;
void clear_graph() {
memset(h, -1, sizeof(h));
idx = 0;
memset(du, 0, sizeof(du));
}
void add_edge(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; }
int topsort() {
priority_queue<int, vector<int>, greater<int> > q;
for (int i = 1; i <= n; i++) {
if (du[i] == 0) {
q.push(i);
}
}
int cnt = 0;
int rounds = 1;
while (q.size()) {
auto t = q.top();
q.pop();
path.push_back(t);
cnt++;
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
du[j]--;
if (du[j] == 0) {
q.push(j);
}
}
}
if (cnt == n) return 1;
return 0;
}
int main() {
cin >> n >> m;
clear_graph();
for (int i = 0, a, b; i < m; i++) {
cin >> a >> b;
add_edge(a, b);
du[b]++;
}
int t = topsort();
if (t == 0)
puts("-1");
else {
for (auto it : path) {
cout << it << " ";
}
}
}
C - Doukasen
链接: link.
题意:
有 N N N根引线,每个引线都有长度和燃烧时间,现在在左和右都点燃引线,问会在长度为多少的地方结束燃烧
思路:
先算一遍从一个端点燃烧的总时间,此时两个点同时燃烧的总时间就是一个端点总时间的一半,然后根据时间从左往右在跑一边燃烧直至时间为0,看停在哪个点
#include <algorithm>
#include <cmath<以上是关于AtCoder Beginner Contest 223(补题)的主要内容,如果未能解决你的问题,请参考以下文章
AtCoder Beginner Contest 115 题解