Codeforces Educational Codeforces Round 67

目录

  • Contest Info
  • Solutions
    • A. Stickers and Toys
    • B. Letters Shop
    • C. Vasya And Array
    • D. Subarray Sorting
    • E. Tree Painting

Contest Info



Data:2019.6.30
Solved:4/7

Solutions


A. Stickers and Toys

题意:
有\(A\)物品\(s\)个,\(B\)物品\(t\)个,现在将这些物品装到\(n\)个箱子里,每个箱子只有一下三种情况:

  • 只有一个\(A\)物品
  • 只有一个\(B\)物品
  • 有一个\(A\)物品和一个\(B\)物品

现在问你,至少要取多少个箱子,能够保证你最少有一个\(A\)物品和一个\(B\)物品。

思路:
根据鸽笼原理,显然对于\(A\)物品,至少取\(n - s + 1\)个箱子就可以有一个\(A\)物品。
同理,对于\(B\)物品至少要取\(n - t + 1\)个箱子。
答案就是\(Min(n - s +1, n - t + 1)\)

代码:

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, s, t;
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d", &n, &s, &t);
        int res = max(n - s + 1, n - t + 1);
        printf("%d\n", res);
    }
    return 0;
}

B. Letters Shop

题意:
有一个字符串\(s\),每次询问一个字符串\(t\),问最短的一个\(s\)的前缀使得这个前缀中拥有的字符可以组成字符串\(t\)。

思路一:
可以维护一个字符个数的前缀和,然后二分。

代码一:

#include <bits/stdc++.h>
using namespace std;

#define N 200010
int n, m, lens, lent;
char s[N], t[N];
int sum[N][27];
int cnt[27];

bool ok(int x) {
    for (int i = 0; i < 26; ++i) {
        if (sum[x][i] < cnt[i]) {
            return 0;
        }
    }
    return 1;
}

int main() {
    while (scanf("%d", &n) != EOF) {
        memset(sum, 0, sizeof sum);
        scanf("%s", s + 1); lens = strlen(s + 1);
        for (int i = 1; i <= lens; ++i) {
            ++sum[i][s[i] - 'a'];
            for (int j = 0; j < 26; ++j) {
                sum[i][j] += sum[i - 1][j];
            }
        }
        scanf("%d", &m);
        while (m--) {
            scanf("%s", t + 1);  lent = strlen(t + 1);
            memset(cnt, 0, sizeof cnt);
            for (int i = 1; i <= lent; ++i) {
                ++cnt[t[i] - 'a'];
            }
            int l = 1, r = n, res = -1;
            while (r - l >= 0) {
                int mid = (l + r) >> 1;
                if (ok(mid)) {
                    r = mid - 1;
                    res = mid;
                } else {
                    l = mid + 1;
                }
            }
            printf("%d\n", res);
        }
    }
    return 0;
}

思路二:
维护\(s\)串中某类字符的第\(i\)个所在位置,显然对于\(t\)串中的每类字符有\(x\)个的话,\(s\)串前缀的长度要大于等于这类字符第\(x\)个所在的位置。

代码二:

#include <bits/stdc++.h>
using namespace std;

#define N 200010
int n, m, lens, lent;
char s[N], t[N];
int sum[N][27];
int cnt[27];

bool ok(int x) {
    for (int i = 0; i < 26; ++i) {
        if (sum[x][i] < cnt[i]) {
            return 0;
        }
    }
    return 1;
}

int main() {
    while (scanf("%d", &n) != EOF) {
        memset(sum, 0, sizeof sum);
        scanf("%s", s + 1); lens = strlen(s + 1);
        for (int i = 1; i <= lens; ++i) {
            ++sum[i][s[i] - 'a'];
            for (int j = 0; j < 26; ++j) {
                sum[i][j] += sum[i - 1][j];
            }
        }
        scanf("%d", &m);
        while (m--) {
            scanf("%s", t + 1);  lent = strlen(t + 1);
            memset(cnt, 0, sizeof cnt);
            for (int i = 1; i <= lent; ++i) {
                ++cnt[t[i] - 'a'];
            }
            int l = 1, r = n, res = -1;
            while (r - l >= 0) {
                int mid = (l + r) >> 1;
                if (ok(mid)) {
                    r = mid - 1;
                    res = mid;
                } else {
                    l = mid + 1;
                }
            }
            printf("%d\n", res);
        }
    }
    return 0;
}

C. Vasya And Array

题意:
要求构造一个数列\(a_1, \cdots, a_n\),使得满足\(m\)个限制。
限制有两种类型:

  • 1 l r 表示\([l, r]\)范围内的数是非降序的
  • 0 l r 表示\([l, r]\)范围内的数不是非降序的

给出构造结果,或者输出‘NO’表示不存在这样的数列。

思路:
显然非降序的\([l, r]\),我们可以全都赋为\(1\),但是最后一位可以不用赋为\(1\)。
然后将没有赋为\(1\)的地方降序赋值。
再考虑不是非降序的,只要满足这个区间内存在一个\(i\)满足\(a_i > a_{i + 1}\)即可。
只要check一下这些限制的区间内是否有这样一对即可。
否则输出‘NO‘

因为没考虑这样的对在最后一位的情况被HACK了。

代码:

#include <bits/stdc++.h>
using namespace std;

#define N 1010
int n, m;
int s[N];
struct node {
    int t, l, r;
    node() {}
    void scan() {
        scanf("%d%d%d", &t, &l, &r);
    }
}a[N];

bool ok(int l, int r) {
    for (int i = l; i <= r; ++i) {
        if (s[i] == 0) {
            return 1;
        }
    }
    return 0;
}

bool check(node a) {
    if (a.t == 1) {
        for (int i = a.l + 1; i <= a.r; ++i) {
            if (s[i - 1] > s[i]) {
                return 0;
            }
        }
        return 1;
    } else {
        for (int i = a.l + 1; i <= a.r; ++i) {
            if (s[i - 1] > s[i]) {
                return 1;
            }
        }
        return 0;
    }
}

void work() {
    memset(s, 0, sizeof s);
    for (int i = 1; i <= m; ++i) {
        if (a[i].t == 1) {
            ++s[a[i].l];
            --s[a[i].r];
        }
    }
    for (int i = 1; i <= n; ++i) s[i] += s[i - 1];
    for (int i = 1; i <= n; ++i) {
        if (s[i]) s[i] = 1;
    }
    for (int i = 1; i <= m; ++i) {
        if (a[i].t == 0) {
            if (!ok(a[i].l, a[i].r)) {
                puts("NO");
                return;
            }
        }
    }
    int cnt = n;
    for (int i = 1; i <= n; ++i) {
        if (s[i] == 0) {
            s[i] = cnt;
            --cnt;
        }
    }
    for (int i = 1; i <= m; ++i) {
        if (!check(a[i])) {
            puts("NO");
            return;
        }
    }
    puts("YES");
    for (int i = 1; i <= n; ++i) printf("%d%c", s[i], " \n"[i == n]);
}

int main() {
    while (scanf("%d%d", &n, &m) != EOF) {
        for (int i = 1; i <= m; ++i) {
            a[i].scan();
        }
        work();
    }
    return 0;
}

D. Subarray Sorting

题意:
给出两个数组\(a_1, \cdots, a_n\), \(b_1, \cdots, b_n\),可以将\(a\)数组进行不限次数的区间排序,问能够变成\(b\)数组。

思路:
考虑从左往右移动\(a\)中的数使得满足\(a_i = b_i\), 我们发现对于我们需要的\(a_i\),它能移动过来当且仅当它之前不存在比它小的数,
权值线段树维护一下即可。

代码:

#include <bits/stdc++.h>
using namespace std;

#define N 300010
int n, a[N], b[N];
int cnt[N], nx[N], f[N];

struct SEG {
    int a[N << 2];
    void build(int id, int l, int r) {
        a[id] = 1e9;
        if (l == r) return;
        int mid = (l + r) >> 1;
        build(id << 1, l, mid);
        build(id << 1 | 1, mid + 1, r);
    }
    void update(int id, int l, int r, int pos, int x) {
        if (l == r) {
            a[id] = x;
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) update(id << 1, l, mid, pos, x);
        else update(id << 1 | 1, mid + 1, r, pos, x);
        a[id] = min(a[id << 1], a[id << 1 | 1]);
    }
    int query(int id, int l, int r, int ql, int qr) {
        if (ql > qr) return 1e9;
        if (l >= ql && r <= qr) {
            return a[id];
        }
        int mid = (l + r) >> 1;
        int res = 1e9;
        if (ql <= mid) res = min(res, query(id << 1, l, mid, ql, qr));
        if (qr > mid) res = min(res, query(id << 1 | 1, mid + 1, r, ql, qr));
        return res;
    }
}seg;

bool work() {
    for (int i = 1; i <= n; ++i) {
        cnt[i] = 0;
    }
    for (int i = 1; i <= n; ++i) {
        ++cnt[a[i]];
        --cnt[b[i]];
    }
    for (int i = 1; i <= n; ++i) {
        if (cnt[i] != 0) {
            return 0;
        }
    }
    seg.build(1, 1, n);
    for (int i = n; i >= 1; --i) {
        nx[i] = n + 1;
    }
    for (int i = n; i >= 1; --i) {
        f[i] = nx[a[i]];
        nx[a[i]] = i;
    }
//  for (int i = 1; i <= n; ++i) {
//      printf("%d %d\n", i, nx[i]);
//  }
    for (int i = 1; i <= n; ++i) {
        seg.update(1, 1, n, i, nx[i]);
    }
    for (int i = 1; i <= n; ++i) {
        if (seg.query(1, 1, n, 1, b[i] - 1) < nx[b[i]]) return 0;
        nx[b[i]] = f[nx[b[i]]];
        seg.update(1, 1, n, b[i], nx[b[i]]);
    }
    return 1;
}

int main() {
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
        }
        for (int i = 1; i <= n; ++i) {
            scanf("%d", b + i);
        }
        puts(work() ? "YES" : "NO");
    }
    return 0;
}

E. Tree Painting

题意:
有一种树上游戏,刚开始每个点为黑点,第一次可以先选择一个点染白,之后每一次都可以选择一个与白点相邻的黑点将其染白,获得的分数为这个黑点所在的由黑点构成的连通块大小。
问在最优策略下获得的最大分数是多少?

思路:
考虑到根固定的话,选择的固定的,即每次从根往下取,而不是隔层取。
树形DP即可。

代码:

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define N 200010
int n;
vector <vector<int>> G;
int fa[N], sze[N];
ll f[N], g[N], res;
void DFS(int u) {
    sze[u] = 1;
    f[u] = 0;
    for (auto v : G[u]) if (v != fa[u]) {
        fa[v] = u;
        DFS(v);
        sze[u] += sze[v];
        f[u] += f[v];
    }
    f[u] += sze[u];
}

void DFS2(int u) {
    if (u == 1) {
        g[u] = 0;
    } else {
        g[u] = g[fa[u]] + f[fa[u]] - f[u] - sze[u] + n - sze[fa[u]];
        res = max(res, f[u] + g[u] - sze[u] + n);
    }
    for (auto v : G[u]) if (v != fa[u]) {
        DFS2(v);
    }
}

int main() {
    while (scanf("%d", &n) != EOF) {
        G.clear(); G.resize(n + 1);
        for (int i = 1, u, v; i < n; ++i) {
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        DFS(1);
        res = f[1];
        DFS2(1);
        printf("%lld\n", res);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Dup4/p/11116198.html

时间: 2024-10-09 05:10:10

Codeforces Educational Codeforces Round 67的相关文章

Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes

Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes 题目连接: http://codeforces.com/contest/985/problem/E Description Mishka received a gift of multicolored pencils for his birthday! Unfortunately he lives in a monochrome w

Codeforces Educational Codeforces Round 15 C. Cellular Network

C. Cellular Network time limit per test 3 seconds memory limit per test 256 megabytes input standard input output standard output You are given n points on the straight line — the positions (x-coordinates) of the cities and m points on the same line

codeforces Educational Codeforces Round 5 A. Comparing Two Long Integers

题目链接:http://codeforces.com/problemset/problem/616/A 题目意思:顾名思义,就是比较两个长度不超过 1e6 的字符串的大小 模拟即可.提供两个版本,数组版本 & 指针版本. (1)数组版本(短的字符串从高位处补0,直到跟长的字符串长度相同) 1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring>

(最小生成树)Codeforces Educational Codeforces Round 9 Magic Matrix

You're given a matrix A of size n?×?n. Let's call the matrix with nonnegative elements magic if it is symmetric (so aij?=?aji), aii?=?0 and aij?≤?max(aik,?ajk) for all triples i,?j,?k. Note that i,?j,?k do not need to be distinct. Determine if the ma

(KMP、dp)Codeforces Educational Codeforces Round 21 G-Anthem of Berland

Berland has a long and glorious history. To increase awareness about it among younger citizens, King of Berland decided to compose an anthem. Though there are lots and lots of victories in history of Berland, there is the one that stand out the most.

Codeforces Educational Codeforces Round 54 题解

题目链接:https://codeforc.es/contest/1076 A. Minimizing the String 题意:给出一个字符串,最多删掉一个字母,输出操作后字典序最小的字符串. 题解:若存在一个位置 i 满足 a[i] > a[i+1],若不删除 a[i] 则后续操作不可能更优. 1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define ull unsigned

codeforces Educational Codeforces Round 2 C Make Palindrome

C. Make Palindrome A string is called palindrome if it reads the same from left to right and from right to left. For example "kazak", "oo", "r" and "mikhailrubinchikkihcniburliahkim" are palindroms, but strings &quo

[Codeforces]Educational Codeforces Round 37 (Rated for Div. 2)

Water The Garden #pragma comment(linker, "/STACK:102400000,102400000") #include<stdio.h> #include<string.h> #include<stdlib.h> #include<vector> #include<algorithm> #include<iostream> #include<map> #inclu

codeforces Educational Codeforces Round 9 E - Thief in a Shop

E - Thief in a Shop 题目大意:给你n ( n <= 1000)个物品每个物品的价值为ai (ai <= 1000),你只能恰好取k个物品,问你能组成哪些价值. 思路:我们很容易能够想到dp[ i ][ j ]表示取i次j是否存在,但是复杂度1e12肯定不行. 我们将ai排序,每个值都减去a[1]然后再用dp[ i ]表示到达i这个值最少需要取几次,只需要1e9就能完成, 我们扫一遍dp数组,如果dp[ i ]  <= k 则说明 i + k * a[1]是能取到的.