zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 6

    题目链接:https://codeforces.com/contest/616

    A - Professor GukiZ's Robot

    送分题。

    *B - Grandfather Dovlet’s calculator

    题意:求L,R范围内的[0,9]的数字的出现次数。

    这题好像可以数位dp,把10种数字分开统计。虽然这个数据范围没必要,但是想写一下这个解法。

    数位dp解法:需要注意,当lead标记存在时,统一不统计0的出现次数(即使这个数就是0,也不计算,为了简化程序),然后就是巨复杂的limit标记。当limit标记存在且i==d[pos],则有后面的数位仍然受限,例如:123456,确定了123???,则???的范围是[000,456],要找十进制下低位余数+1。否则,例如123456,确定了122???,则???的范围是[000,999]。

    int k;
    ll p10[17];
    
    int d[17];
    ll r[17];
    ll dp[17];
    
    ll dfs(int pos, bool lead, bool limit) {
        if(pos == 0)
            return 0;
        if(!lead && !limit && dp[pos] != -1)
            return dp[pos];
        int up = limit ? d[pos] : 9;
        ll ans = 0;
        for(int i = 0; i <= up; i++) {
            ll D = dfs(pos - 1, lead && i == 0, limit && i == d[pos]);
            ans += D;
            if(i == k) {
                if(lead && i == 0)
                    continue;
                if(limit && i == d[pos])
                    ans += r[pos - 1] + 1;
                else
                    ans += p10[pos - 1];
            }
        }
        if(!lead && !limit)
            dp[pos] = ans;
        return ans;
    }
    
    ll solve(ll x) {
        if(x < 0)
            return 0;
        int pos = 0;
        ll cx = x;
        while(cx) {
            d[++pos] = cx % 10;
            cx /= 10;
        }
        for(int i = 1; i <= pos; ++i)
            r[i] = x % p10[i];
        return dfs(pos, true, true);
    }
    
    int cost[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
    
    void test_case() {
        p10[0] = 1;
        for(int i = 1; i < 17; ++i)
            p10[i] = p10[i - 1] * 10;
        ll L, R;
        scanf("%lld%lld", &L, &R);
        ll sum = 0;
        for(k = 0; k <= 9; ++k) {
            memset(dp, -1, sizeof(dp));
            ll cnt = solve(R) - solve(L - 1);
            sum += cnt * cost[k];
        }
        printf("%lld
    ", sum);
    }
    

    暴力写法:

    int cnt[10];
    int cost[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
    
    void test_case() {
        int L, R;
        scanf("%d%d", &L, &R);
        for(int i = L; i <= R; ++i) {
            int x = i;
            while(x) {
                ++cnt[x % 10];
                x /= 10;
            }
        }
        ll sum = 0;
        for(int k = 0; k <= 9; ++k)
            sum += cnt[k] * cost[k];
        printf("%lld
    ", sum);
    }
    

    *C - Pearls in a Row

    题意:给一个数组,切分成尽可能多的段,使得每一段都至少有两个相同的元素。

    是个简单的贪心尺取,每次有两个相同的就切一段。注意最后一段没有被成功切下来的,不能独立成段,而是合并到最后一段成功切下来的段中。因为题目要求每一段都要是成功切下来的段。

    离散化又搞错xtop和n。

    int a[300005];
    int x[300005], xtop;
    int cnt[300005];
    
    int L[300005];
    int R[300005];
    int top;
    
    void test_case() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            x[i] = a[i];
        }
        sort(x + 1, x + 1 + n);
        xtop = unique(x + 1, x + 1 + n) - (x + 1);
        for(int i = 1; i <= n; ++i)
            a[i] = lower_bound(x + 1, x + 1 + xtop, a[i]) - x;
        top = 0;
        int l = 1;
        for(int i = 1; i <= n; ++i) {
            ++cnt[a[i]];
            if(cnt[a[i]] == 2) {
                ++top;
                L[top] = l;
                R[top] = i;
                for(int j = l; j <= i; ++j)
                    cnt[a[j]] = 0;
                l = i + 1;
            }
        }
        if(top == 0) {
            puts("-1");
            return;
        }
        if(l <= n)
            R[top] = n;
        printf("%d
    ", top);
        for(int i = 1; i <= top; ++i)
            printf("%d %d
    ", L[i], R[i]);
    }
    

    好像set没有多慢,才比上面的慢50%。

    int a[300005];
    set<int> s;
    
    int L[300005];
    int R[300005];
    int top;
    
    void test_case() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        top = 0;
        int l = 1;
        for(int i = 1; i <= n; ++i) {
            if(s.count(a[i])) {
                ++top;
                L[top] = l;
                R[top] = i;
                s.clear();
                l = i + 1;
            } else
                s.insert(a[i]);
        }
        if(top == 0) {
            puts("-1");
            return;
        }
        if(l <= n)
            R[top] = n;
        printf("%d
    ", top);
        for(int i = 1; i <= top; ++i)
            printf("%d %d
    ", L[i], R[i]);
    }
    

    所以还是直接用set算了。

    *D - Professor GukiZ and Two Arrays

    题意:给n(<=2e3)个数字的数组a,和m(<=2e3)个数字的数组b。交换不超过2次,最小化数组a和数组b的和的差的绝对值。每次交换可以把一个数组a的元素和一个数组b的元素交换。

    题解:交换0次的,可以直接一边扫出来,交换1次的,可以O(nm)枚举是哪一对发生了交换,立刻扫出来。交换2次的,易知选取的是两对不同的元素。看一下数据范围,考虑使用数据结构。枚举a中的所有的pair,把他们的和的两倍2(ai+aj)作为第一关键字插进数据结构Map中。然后再枚举b中所有的pair。假如一开始的和是sa和sb,那么bi+bj换出之后,要换入哪个ai+aj才是差的绝对值最小呢?易知新的b的和为sb'=sb-(bi+bj)+(ai+aj),新的a的和为sa'=sa-(ai+aj)+(bi+bj),差值d=|sa-sb-2(ai+aj)+2(bi+bj)|,要最小化这个,就应该使得sa-sb+2(bi+bj)与2(ai+aj)尽可能接近。换言之应该在Map中lower_bound找到第一个大于等于的。若等于则直接构造出差值为0的解,否则一定是大于,就把得到的迭代器和前一个迭代器都取出来看看。

    Map的写法:

    int n, m;
    int a[2005], b[2005];
    map<ll, pair<int, int> >Map;
     
    void test_case() {
        scanf("%d", &n);
        ll sa = 0, sb = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            sa += a[i];
        }
        scanf("%d",&m);
        for(int j = 1; j <= m; ++j) {
            scanf("%d", &b[j]);
            sb += b[j];
        }
        ll dis = abs(sa - sb);
        if(dis == 0) {
            puts("0");
            puts("0");
            return;
        }
        int k = 0, k1 = -1, k2 = -1, k3 = -1, k4 = -1;
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= m; ++j) {
                ll tmpdis = abs(sa - sb - 2ll * a[i] + 2ll * b[j]);
                if(tmpdis == 0) {
                    puts("0");
                    puts("1");
                    printf("%d %d
    ", i, j);
                    return;
                }
                if(tmpdis < dis) {
                    dis = tmpdis;
                    k = 1;
                    k1 = i;
                    k2 = j;
                }
            }
        }
        for(int i = 1; i <= n; ++i) {
            for(int j = i + 1; j <= n; ++j)
                Map[2ll * (a[i] + a[j])] = {i, j};
        }
        for(int i = 1; i <= m; ++i) {
            for(int j = i + 1; j <= m; ++j) {
                ll tmp = sa - sb + 2ll * (b[i] + b[j]);
                auto it = Map.lower_bound(tmp);
                if(it != Map.end()) {
                    ll tmpdis = abs(it->first - tmp);
                    if(tmpdis < dis) {
                        dis = tmpdis;
                        k = 2;
                        k1 = it->second.first;
                        k2 = i;
                        k3 = it->second.second;
                        k4 = j;
                    }
                }
                if(it != Map.begin()) {
                    --it;
                    ll tmpdis = abs(it->first - tmp);
                    if(tmpdis < dis) {
                        dis = tmpdis;
                        k = 2;
                        k1 = it->second.first;
                        k2 = i;
                        k3 = it->second.second;
                        k4 = j;
                    }
                }
            }
        }
        printf("%lld
    ", dis);
        printf("%d
    ", k);
        if(k == 0)
            return;
        if(k == 1) {
            printf("%d %d
    ", k1, k2);
            return;
        }
        if(k == 2) {
            printf("%d %d
    ", k1, k2);
            printf("%d %d
    ", k3, k4);
            return;
        }
    }
    

    可能常数会好一点(3.5倍速)的写法:

    int n, m;
    int a[2005], b[2005];
    pair<ll, pii> Map[4000005];
    ll x[4000005];
     
    void test_case() {
        scanf("%d", &n);
        ll sa = 0, sb = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            sa += a[i];
        }
        scanf("%d", &m);
        for(int j = 1; j <= m; ++j) {
            scanf("%d", &b[j]);
            sb += b[j];
        }
        ll dis = abs(sa - sb);
        if(dis == 0) {
            puts("0");
            puts("0");
            return;
        }
        int k = 0, k1 = -1, k2 = -1, k3 = -1, k4 = -1;
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= m; ++j) {
                ll tmpdis = abs(sa - sb - 2ll * a[i] + 2ll * b[j]);
                if(tmpdis == 0) {
                    puts("0");
                    puts("1");
                    printf("%d %d
    ", i, j);
                    return;
                }
                if(tmpdis < dis) {
                    dis = tmpdis;
                    k = 1;
                    k1 = i;
                    k2 = j;
                }
            }
        }
        int Mtop = 0;
        for(int i = 1; i <= n; ++i) {
            for(int j = i + 1; j <= n; ++j)
                Map[++Mtop] = make_pair(2ll * (a[i] + a[j]), make_pair(i, j));
        }
        sort(Map + 1, Map + 1 + Mtop);
        for(int i = 1; i <= Mtop; ++i)
            x[i] = Map[i].first;
        for(int i = 1; i <= m; ++i) {
            for(int j = i + 1; j <= m; ++j) {
                ll tmp = sa - sb + 2ll * (b[i] + b[j]);
                int pos = lower_bound(x + 1, x + 1 + Mtop, tmp) - x;
                if(pos <= Mtop) {
                    ll tmpdis = abs(Map[pos].first - tmp);
                    if(tmpdis < dis) {
                        dis = tmpdis;
                        k = 2;
                        k1 = Map[pos].second.first;
                        k2 = i;
                        k3 = Map[pos].second.second;
                        k4 = j;
                    }
                }
                if(pos != 1) {
                    --pos;
                    ll tmpdis = abs(Map[pos].first - tmp);
                    if(tmpdis < dis) {
                        dis = tmpdis;
                        k = 2;
                        k1 = Map[pos].second.first;
                        k2 = i;
                        k3 = Map[pos].second.second;
                        k4 = j;
                    }
                }
            }
        }
        printf("%lld
    ", dis);
        printf("%d
    ", k);
        if(k == 0)
            return;
        if(k == 1) {
            printf("%d %d
    ", k1, k2);
            return;
        }
        if(k == 2) {
            printf("%d %d
    ", k1, k2);
            printf("%d %d
    ", k3, k4);
            return;
        }
    }
    

    实验证明这个Map确实是有够慢的,假如不需要修改的话还是排序之后二分最实在。

    *E - New Year Tree

    看起来很线段树的一道题。

    题意:一棵以1为根的数,每个点带有一种颜色。每次操作:操作1:把一棵v节点下属的子树的颜色全部改变成c;或者操作2:询问一棵v节点下属的子树的颜色的种类。注意只有至多60种颜色。

    题解:显然可以按树的dfs序建立线段树。每次对v节点改颜色,就找到v节点子树的范围,进行60次区间改值。然后对于v节点查颜色,就进行60次区间查询。但是感觉复杂度很爆炸。原来这题的idea是用64位整数存这个区间的信息。这样就节约了60次重复的操作。

    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 400000;
        ll st[(MAXN << 2) + 5];
        ll lazy[(MAXN << 2) + 5];
    
        void PushUp(int o) {
            st[o] = st[ls] | st[rs];
        }
    
        void PushDown(int o, int l, int r) {
            if(lazy[o]) {
                lazy[ls] = lazy[o];
                lazy[rs] = lazy[o];
                int m = l + r >> 1;
                st[ls] = lazy[o];
                st[rs] = lazy[o];
                lazy[o] = 0;
            }
        }
    
        void Build(int o, int l, int r) {
            if(l == r)
                st[o] = 0;
            else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
            }
            lazy[o] = 0;
        }
    
        void Update(int o, int l, int r, int ql, int qr, ll v) {
            if(ql <= l && r <= qr) {
                lazy[o] = v;
                st[o] = v;
                return;
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                if(ql <= m)
                    Update(ls, l, m, ql, qr, v);
                if(qr >= m + 1)
                    Update(rs, m + 1, r, ql, qr, v);
                PushUp(o);
            }
        }
    
        ll Query(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return st[o];
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                ll res = 0;
                if(ql <= m)
                    res = Query(ls, l, m, ql, qr);
                if(qr >= m + 1)
                    res = res | Query(rs, m + 1, r, ql, qr);
                return res;
            }
        }
    #undef ls
    #undef rs
    } st;
    
    int n, q;
    int v[400005];
    vector<int> G[400005];
    
    int L[400005], R[400005], dtop;
    
    void dfs(int u, int p) {
        L[u] = ++dtop;
        for(auto &v : G[u]) {
            if(v == p)
                continue;
            dfs(v, u);
        }
        R[u] = dtop;
    }
    
    void test_case() {
        scanf("%d%d", &n, &q);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &v[i]);
            --v[i];
        }
        for(int i = 1; i <= n - 1; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dtop = 0;
        dfs(1, 0);
        st.Build(1, 1, n);
        for(int i = 1; i <= n; ++i)
            st.Update(1, 1, n, L[i], L[i], 1ll << v[i]);
        while(q--) {
            int op, p, c;
            scanf("%d%d", &op, &p);
            if(op == 1) {
                scanf("%d", &c);
                --c;
                st.Update(1, 1, n, L[p], R[p], 1ll << c);
            } else {
                ll res = st.Query(1, 1, n, L[p], R[p]);
                int cnt = 0;
                while(res) {
                    cnt += (res & 1);
                    res >>= 1;
                }
                printf("%d
    ", cnt);
            }
        }
    }
    

    提示:
    1、dfs序,可以在进入u的时候,把区间左端点标记为u的dfn序,然后遍历其子树,当从其子树返回时,当前的dfn序就是右端点。也就是:

    int L[400005], R[400005], dtop;
    
    void dfs(int u, int p) {
        L[u] = ++dtop;
        for(auto &v : G[u]) {
            if(v == p)
                continue;
            dfs(v, u);
        }
        R[u] = dtop;
    }
    

    2、只需要知道某颜色是否存在,这个用一个bit就可以表示。

    *F - Xors on Segments

    这个数据量看起来很像莫队算法。

    题意:定义一种函数 (f(l,r)=l oplus l+1 oplus ... oplus r) ,这个函数仅当 (l leq r) 时有定义。给一个 (n(nleq 5 cdot 10^4))数组 (a)(a_i leq 10^6) ,询问 (m(leq 5 cdot 10^3)) 次,每次询问一对 (x,y) ,求出 $maxlimits_{i=x,j=x}^{i leq y,j leq y} f(a_i,a_j) (a_i leq a_j) $

  • 相关阅读:
    Linux考试题附答案
    MariaDB数据库主从复制实现步骤
    LinuxCentos系统安装Mariadb过程记录
    LinuxCentos系统安装Nginx过程记录
    VMware虚拟机不能联网的解决办法
    Linux centos下设置定时备份任务
    如何修改本地hosts文件?
    mysql用户授权以及权限收回
    Ubuntu系统下完全卸载和安装Mysql
    C++之类和对象的使用(一)
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12486160.html
Copyright © 2011-2022 走看看