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, '
');
}
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 eq y)),使得(a_x = leftlceil frac{a_x}{a_y} ight ceil)。
要求执行不多于(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(), '
');
for(auto i : ans){
printf("%d %d
", 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} ceil = 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
";
continue;
}else{
cout << "YES
";
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没炸
神奇的代码
/*
* Author: Lanly
* Time: 2020-12-30 12:04:44
*/
#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, '
');
return 0;
}