Educational Codeforces Round 101 (Rated for Div. 2)
A. Regular Bracket Sequence (CF 1469 A)
题目大意
给定一个包含若干个和分别一个的、的字符串,问能否将变成或,是字符串成为一个合法的括号字符串。
解题思路
注意只有一个和
长度奇数不可。
于首位不可。
于末尾不可。
其余均可。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { string a; cin >> a; if (a[0] == ')'){ cout << "NO" << endl; continue; } if (a[a.size() - 1] == '('){ cout << "NO" << endl; continue; } if (a.size() & 1) cout << "NO" << endl; else cout << "YES" << endl; } return 0; }
B. Red and Blue (CF 1469 B)
题目大意
给定两个数组和,现在要求组成一个数组,其中是里的元素的相对位置不变,同理,使得的前缀和最大值最大。求最大值。
解题思路
注意到最大值就来自于的某前缀和加上的某前缀和,枚举求最值就可以了。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { int n; read(n); vector<int> a(n + 1); for(int i = 1; i <= n; ++ i) read(a[i]); int m; read(m); vector<int> b(m + 1); for(int i = 1; i <= m; ++ i) read(b[i]); vector<int> suma(n + 1), sumb(m + 1); suma[0] = sumb[0] = 0; for(int i = 1; i <= n; ++ i) suma[i] = a[i] + suma[i - 1]; for(int i = 1; i <= m; ++ i) sumb[i] = b[i] + sumb[i - 1]; int ans = 0; for(int i = 0; i <= n; ++ i){ for(int j = 0; j <= m; ++ j){ ans = max(ans, suma[i] + sumb[j]); } } write(ans, '\n'); } return 0; }
C. Building a Fence (CF 1469 C)
题目大意
给了个宽为,高为的栅栏,以及凹凸不平的地,长度为,先要求将栅栏放到这地上,要求
- 第一个栅栏和最后一个栅栏只能放到地上
- 中间的栅栏最多可以高于地面的高度放置
- 相邻栅栏至少有高度1的单位是毗邻的
问是否存在放置要求满足以上方案。
解题思路
第一个栅栏的放置高度是固定的,进而第二个栅栏放置高度有确定的范围,且下一个栅栏可放置的高度范围只与前一个栅栏有关,所以就直接维护可以当前栅栏放置高度的范围,为当前允许范围交上上一个栅栏的限制范围,到最后一个栅栏看看放置的高度在不在范围内即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { int n, k; read(n); read(k); vector<LL> qwq(n); for(auto &i : qwq) read(i); LL l = qwq[0], r = qwq[0] + k; int sign = true; for(int i = 1; i < n - 1; ++ i){ if (l >= qwq[i] + k - 1 + k) sign = false; if (r <= qwq[i]) sign = false; l = max(qwq[i], l - k + 1); r = min(r - 1, qwq[i] + k - 1) + k; } if (l >= qwq[n - 1] + k) sign = false; if (r <= qwq[n - 1]) sign = false; if (sign) puts("YES"); else puts("NO"); } return 0; }
D. Ceil Divisions (CF 1469 D)
题目大意
给定一个有个数的数组,每次操作选择两个数,使得。
要求执行不多于个操作,将数组变成有n-1$个和个的数组。输出依次进行的操作,不要求最小化。
解题思路
自然的想法就是把分别取,,最后取,,但这样的操作数是,较大时,超过了。
因为为,为的操作数过多,我们设法让变大一点,比如取,这样就不超过5了,而也最多,多试几个数就能过了。(想精确的可以列出式子求个最值)
也就是,,然后,,直到第个数变成,然后,,直到第个数变成。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { int n; read(n); vector<pair<int,int>> ans; int base = 15; for(int i = 3; i < n; ++ i){ if (i == base) continue; ans.push_back({i, n}); } int qwq = n; if (n > base){ while(n != 1){ ans.push_back({qwq, base}); n = ceil(n * 1.0 / base); } int x = base; while(x != 1){ ans.push_back({base, 2}); x = ceil(x * 1.0 / 2); } }else{ while(n != 1){ ans.push_back({qwq, 2}); n = ceil(n * 1.0 / 2); } } write(ans.size(), '\n'); for(auto i : ans){ printf("%d %d\n", i.first, i.second); } } return 0; }
E. A Bit Similar (CF 1469 E)
题目大意
给定一个长度为的字符串,要求构造一个长度为的字符串,使得与每一个长度为的子串都有一点相似。求字典序最小的字符串。
两个长度为的字符串,如果存在一位,有,则这两个字符串有一点相似
解题思路
考虑串的第一个位置能否填,取决于剩下位能够使得和所有子串有一点相似,而这样考虑的话我们得储存已经和哪些子串有一点相似的信息,不行。
由于有一点相似的条件是任意的一位,我们考虑它的逆否命题,即不能是该子串的翻转子串。
那么原题就变成给出了个被禁止的子串,求未被禁止的字典序最小的串是什么。
虽然有,但我们注意到,也就是说它最多能禁止20位的子串,所以我们只用考虑长度最长为的子串,剩下的位全部为即可。
所以我们就找出这个被禁止的子串的后位,把它成一个数,丢到禁止列表里,最后再看最小的不在禁止列表里的数是多少即可。
值得注意的是,由于我们前位全部为,所以只有当该子串的前位全部为时,我们才把后位子串丢进禁止列表里,因为如果有的话,本身就有一点相似,不受后位限制。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int kase; cin>>kase; for (int ii = 1; ii <= kase; ii++) { int n, k; string s; cin >> n >> k >> s; int len = min(k, 20); int require = k - len; int st = k - len + 1; unordered_set<int> sign; int cur = 0; int cnt = 0; for(int i = 0; i < st - 1; ++ i) cnt += (s[i] - '0') == 0; for(int i = st - 1; i < st + len - 1; ++ i){ cur <<= 1; cur |= (s[i] - '0') ^ 1; } if (require == 0 || cnt == 0) sign.insert(cur); int leadzero = st - 1; for(int i = st + len - 1; i < (int)s.size(); ++ i){ cur <<= 1; cur |= (s[i] - '0') ^ 1; cur &= ((1 << len) - 1); if (require != 0) cnt = cnt - ((s[leadzero - st + 1] - '0') == 0) + ((s[leadzero] - '0') == 0); ++ leadzero; if (require == 0 || cnt == 0) sign.insert(cur); } int ans = 0; while(sign.find(ans) != sign.end()) ++ ans; if (ans == (1 << len)){ cout << "NO\n"; continue; }else{ cout << "YES\n"; string left(k - len, '0'); string right; while(ans){ right+=char((ans & 1) + 48); ans >>= 1; } while((int)right.size() < len) right += '0'; reverse(right.begin(), right.end()); cout << left + right << endl; } } return 0; }
F. Power Sockets (CF 1469 F)
题目大意
给定条链长度分别为,现在有一棵树,只有根节点,白色。
每次,可以选择一条链,选择其中一个点与树上白色的点相连接,连接后这两个点均变成黑色。
现要求选择一些链连接,使得,所有白色点到根节点的距离的第小值最小。输出这个最小值。
树的边权均为1。
解题思路
题目过于迷幻,甚至无从下手。
首先我们先观察一些性质。比如除了根节点,其他点的度数要么是要么是。然而并没有什么用
首先,我们肯定是取链的中间点与树上白色点相连接。
其次,考虑取怎样链。我们发现自然是越长越好。因为链越长,所谓的附着点(白色点)越多。而一个链连上一个附着点,会有所谓到根节点距离代价增1的情况,如果附着点少,可能发生多次附着的情况,这样到根节点距离代价增加的效果会积累,不利于达到最小值。
觉得没什么问题,决定试一发人品贪一波
每次选长度最长的链,附着到距离根节点最近的白色节点,维护答案。
由于是求出第小的,我们就维护一个计数数组,表示距离根节点距离为的白色点有多少个。
从这个数组能找到距离根节点最近的白色点的数量。
然后当一条链附着上去时,需要区间加法。
线段树维护即可。
虽然有,但最理想的情况是左右,所以其实实际第个值的结果不会很大盲开了个1e5没炸
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int up = 1e5 + 8; class Segment_Tree{ #define lson root << 1 #define rson root << 1 | 1 LL sum[up << 2]; LL mark[up << 2]; public: void build(int root, int l, int r){ if (l == r){ sum[root] = mark[root] = 0; if (l == 1) sum[root] = 1; return; } int mid = (l + r) >> 1; build(lson, l, mid); build(rson, mid + 1, r); sum[root] = sum[lson] + sum[rson]; mark[root] = 0; } void pushdown(int root, int l, int r){ int mid = (l + r) >> 1; mark[lson] += mark[root]; sum[lson] += mark[root] * (mid - l + 1); mark[rson] += mark[root]; sum[rson] += mark[root] * (r - mid); mark[root] = 0; } int findleft(int root, int l, int r){ if (l == r){ return l; } pushdown(root, l, r); int mid = (l + r) >> 1; if (sum[lson] > 0) return findleft(lson, l, mid); else return findleft(rson, mid + 1, r); } void update(int root, int l, int r, int ll, int rr, int val){ if (ll <= l && r <= rr){ mark[root] += val; sum[root] += val * (r - l + 1); return; } pushdown(root, l, r); int mid = (l + r) >> 1; if (ll <= mid) update(lson, l, mid, ll, rr, val); if (rr > mid) update(rson, mid + 1, r, ll, rr, val); sum[root] = sum[lson] + sum[rson]; } int query(int root, int l, int r, LL k){ if (l == r){ if (sum[root] < k) return 1e9 + 8; return l; } pushdown(root, l, r); int mid = (l + r) >> 1; if (sum[lson] >= k) return query(lson, l, mid, k); else return query(rson, mid + 1, r, k - sum[lson]); } }Seg; int main(void) { int n; LL k; read(n); read(k); vector<int> l(n); for(auto & i : l) read(i); sort(l.begin(), l.end(), greater<int>()); Seg.build(1, 1, up); int ans = 1e9 + 7; for(auto i : l){ int st = Seg.findleft(1, 1, up); Seg.update(1, 1, up, st, st, -1); int left = (i - 1)/ 2; Seg.update(1, 1, up, st + 2, st + 2 + left - 1, 1); Seg.update(1, 1, up, st + 2, st + 2 + i - 1 - left - 1, 1); int result = Seg.query(1, 1, up, k); ans = min(ans, result); } if (ans == (int)1e9 + 7) ans = 0; -- ans; write(ans, '\n'); return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/14211076.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2019-12-30 Good Bye 2019
2019-12-30 Educational Codeforces Round 79 (Rated for Div. 2)