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)
题目大意
给定两个数组\(a\)和\(b\),现在要求组成一个数组\(c\),其中是\(a\)里的元素的相对位置不变,\(b\)同理,使得\(c\)的前缀和最大值最大。求最大值。
解题思路
注意到最大值就来自于\(a\)的某前缀和加上\(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)
题目大意
给了\(n\)个宽为\(1\),高为\(h\)的栅栏,以及凹凸不平的地,长度为\(n\),先要求将栅栏放到这地上,要求
- 第一个栅栏和最后一个栅栏只能放到地上
- 中间的栅栏最多可以高于地面\(k - 1\)的高度放置
- 相邻栅栏至少有高度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\)个数的\(a\)数组,每次操作选择两个数\(x, y (x \neq y)\),使得\(a_x = \left\lceil \frac{a_x}{a_y} \right\rceil\)。
要求执行不多于\(n + 5\)个操作,将\(a\)数组变成有n-1$个\(1\)和\(1\)个\(2\)的数组。输出依次进行的操作,不要求最小化。
解题思路
自然的想法就是把\(x\)分别取\(3,4,...,n-1\),\(y=n\),最后取\(x=n\),\(y=2\),但这样的操作数是\(n - 3 + \log_{2}n\),\(n\)较大时,超过了\(n + 5\)。
因为\(x\)为\(n\),\(y\)为\(2\)的操作数过多,我们设法让\(y\)变大一点,比如\(y\)取\(15\),这样\(\log_{15}n\)就不超过5了,而\(\log_{2}15\)也最多\(4\),多试几个数就能过了。(想精确的可以列出式子\(\log_{x}n + \log_{2}x\)求个最值)
也就是\(x=3,4,...,b-1,b+1,...,n-1\),\(y=n\),然后\(x=n\),\(y=b\),直到第\(n\)个数变成\(1\),然后\(x=b\),\(y=2\),直到第\(b\)个数变成\(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;
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)
题目大意
给定一个长度为\(n\)的\(01\)字符串\(s\),要求构造一个长度为\(k\)的\(01\)字符串\(t\),使得\(t\)与每一个长度为\(k\)的\(s\)子串\(s[1..k], s[2..k+1], \dots, s[n-k+1..n]\)都有一点相似。求字典序最小的字符串\(t\)。
两个长度为\(k\)的字符串\(a,b\),如果存在一位\(i \in [1, k]\),有\(a_i = b_i\),则这两个字符串有一点相似
解题思路
考虑\(t\)串的第一个位置能否填\(0\),取决于剩下位能够使得和所有子串有一点相似,而这样考虑的话我们得储存已经和哪些子串有一点相似的信息,不行。
由于有一点相似的条件是任意的一位,我们考虑它的逆否命题,即不能是该子串的\(01\)翻转子串。
那么原题就变成给出了\(n - k + 1\)个被禁止的\(01\)子串,求未被禁止的字典序最小的\(01\)串是什么。
虽然\(n\)有\(10^6\),但我们注意到\(\lceil log_{2}10^{6} \rceil = 20\),也就是说它最多能禁止20位的子串,所以我们只用考虑长度最长为\(20\)的子串,剩下的\(k - 20\)位全部为\(0\)即可。
所以我们就找出这\(n - k + 1\)个被禁止的\(01\)子串的后\(20\)位,把它\(hash\)成一个数,丢到禁止列表里,最后再看最小的不在禁止列表里的数是多少即可。
值得注意的是,由于我们前\(k - 20\)位全部为\(0\),所以只有当该子串的前\(k - 20\)位全部为\(1\)时,我们才把后\(20\)位子串丢进禁止列表里,因为如果有\(0\)的话,本身就有一点相似,不受后\(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)
题目大意
给定\(n\)条链长度分别为\(l_1, l_2, ..., l_n\),现在有一棵树,只有根节点,白色。
每次,可以选择一条链,选择其中一个点与树上白色的点相连接,连接后这两个点均变成黑色。
现要求选择一些链连接,使得,所有白色点到根节点的距离的第\(k\)小值最小。输出这个最小值。
树的边权均为1。
解题思路
题目过于迷幻,甚至无从下手。
首先我们先观察一些性质。比如除了根节点,其他点的度数要么是\(2\)要么是\(3\)。然而并没有什么用
首先,我们肯定是取链的中间点与树上白色点相连接。
其次,考虑取怎样链。我们发现自然是越长越好。因为链越长,所谓的附着点(白色点)越多。而一个链连上一个附着点,会有所谓到根节点距离代价增1的情况,如果附着点少,可能发生多次附着的情况,这样到根节点距离代价增加的效果会积累,不利于达到最小值。
觉得没什么问题,决定试一发人品贪一波
每次选长度最长的链,附着到距离根节点最近的白色节点,维护答案。
由于是求出第\(k\)小的,我们就维护一个计数数组\(cnt[i]\),表示距离根节点距离为\(i\)的白色点有多少个。
从这个数组能找到距离根节点最近的白色点的数量。
然后当一条链附着上去时,需要区间加法。
线段树维护即可。
虽然\(k\)有\(10^{9}\),但最理想的情况\(k\)是\(32\)左右,所以其实实际第\(k\)个值的结果不会很大盲开了个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;
}