2020-12-30 13:46阅读: 180评论: 0推荐: 0

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)

题目大意

给定两个数组ab,现在要求组成一个数组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,先要求将栅栏放到这地上,要求

  • 第一个栅栏和最后一个栅栏只能放到地上
  • 中间的栅栏最多可以高于地面k1的高度放置
  • 相邻栅栏至少有高度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(xy),使得ax=axay

要求执行不多于n+5个操作,将a数组变成有n-1$个112的数组。输出依次进行的操作,不要求最小化。

解题思路

自然的想法就是把x分别取3,4,...,n1y=n,最后取x=ny=2,但这样的操作数是n3+log2nn较大时,超过了n+5

因为xny2的操作数过多,我们设法让y变大一点,比如y15,这样log15n就不超过5了,而log215也最多4,多试几个数就能过了。(想精确的可以列出式子logxn+log2x求个最值)

也就是x=3,4,...,b1,b+1,...,n1y=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)

题目大意

给定一个长度为n01字符串s,要求构造一个长度为k01字符串t,使得t与每一个长度为ks子串s[1..k],s[2..k+1],,s[nk+1..n]有一点相似。求字典序最小的字符串t

两个长度为k的字符串a,b,如果存在一位i[1,k],有ai=bi,则这两个字符串有一点相似

解题思路

考虑t串的第一个位置能否填0,取决于剩下位能够使得和所有子串有一点相似,而这样考虑的话我们得储存已经和哪些子串有一点相似的信息,不行。

由于有一点相似的条件是任意的一位,我们考虑它的逆否命题,即不能是该子串的01翻转子串。

那么原题就变成给出了nk+1个被禁止的01子串,求未被禁止的字典序最小的01串是什么。

虽然n106,但我们注意到log2106=20,也就是说它最多能禁止20位的子串,所以我们只用考虑长度最长为20的子串,剩下的k20位全部为0即可。

所以我们就找出这nk+1个被禁止的01子串的后20位,把它hash成一个数,丢到禁止列表里,最后再看最小的不在禁止列表里的数是多少即可。

值得注意的是,由于我们前k20位全部为0,所以只有当该子串的前k20位全部为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条链长度分别为l1,l2,...,ln,现在有一棵树,只有根节点,白色。

每次,可以选择一条链,选择其中一个点与树上白色的点相连接,连接后这两个点均变成黑色。

现要求选择一些链连接,使得,所有白色点到根节点的距离的第k小值最小。输出这个最小值。

树的边权均为1。

解题思路

题目过于迷幻,甚至无从下手。

首先我们先观察一些性质。比如除了根节点,其他点的度数要么是2要么是3然而并没有什么用

首先,我们肯定是取链的中间点与树上白色点相连接。

其次,考虑取怎样链。我们发现自然是越长越好。因为链越长,所谓的附着点(白色点)越多。而一个链连上一个附着点,会有所谓到根节点距离代价增1的情况,如果附着点少,可能发生多次附着的情况,这样到根节点距离代价增加的效果会积累,不利于达到最小值。

觉得没什么问题,决定试一发人品贪一波

每次选长度最长的链,附着到距离根节点最近的白色节点,维护答案。

由于是求出第k小的,我们就维护一个计数数组cnt[i],表示距离根节点距离为i的白色点有多少个。

从这个数组能找到距离根节点最近的白色点的数量。

然后当一条链附着上去时,需要区间加法。

线段树维护即可。

虽然k109,但最理想的情况k32左右,所以其实实际第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;
}


本文作者:~Lanly~

本文链接:https://www.cnblogs.com/Lanly/p/14211076.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(180)  评论(0编辑  收藏  举报
历史上的今天:
2019-12-30 Good Bye 2019
2019-12-30 Educational Codeforces Round 79 (Rated for Div. 2)
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.