• 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, '
    ');                 
        }
        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;
    }
    


  • 相关阅读:
    Docker学习笔记
    Linux学习笔记
    C#
    30分钟掌握 C#7
    30分钟掌握 C#6
    Redmine部署到Windows Azure
    关于企业管理系统集成那些事
    变量内存分配知多少
    流行Java IDE工具大比拼[转]
    pgpool 流复制主从安装与配置(高可用、读写分离)[转]
  • 原文地址:https://www.cnblogs.com/Lanly/p/14211076.html
Copyright © 2020-2023  润新知