zoukankan      html  css  js  c++  java
  • Codeforces Round #598 (Div. 3)

    A - Payment Without Change

    题意:有a个n元硬币和b个1元硬币,求是否能准确表示出S。

    题解:1元硬币全部减去,有个范围,其中是否包含n的倍数。这样太复杂了。还是全部尽可能用n支付,然后剩下的用1补齐。

    void test_case() {
        int a, b, n, S;
        scanf("%d%d%d%d", &a, &b, &n, &S);
        S -= min(a, S / n) * n;
        if(S <= b)
            puts("YES");
        else
            puts("NO");
    }
    

    B - Minimize the Permutation

    题意:给一个排列,每一对格子至多交换一次,求字典序最小的序列。

    题解:每次把最小的尽可能往前冒泡。

    int a[105];
    int pos[105];
    bool vis[105];
    
    void test_case() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            pos[a[i]] = i;
            vis[i] = 0;
        }
        int cur = 1;
        do {
            for(int i = pos[cur]; i >= 2; --i) {
                if(a[i] < a[i - 1] && vis[i] == 0) {
                    swap(a[i], a[i - 1]);
                    vis[i] = 1;
                } else {
                    break;
                }
            }
            ++cur;
        } while(cur <= n);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", a[i], " 
    "[i == n]);
    }
    

    C - Platforms Jumping

    题意:有m块木板,给出它们的长度,越过长n的河,每次可以移动[1,d]格。不能改变木板的相对顺序,可以移动木板,而木板也不能交叉。

    题解:贪心,但是不是从正向贪,因为正向可能会使得木板交叉。先把木板堆在右边,然后需要的时候让他平移到刚刚好能接住的位置,这样保证木板不会交叉的同时自己能跳最远。当预定的跳的地点出现木板时,剩下的就是一串长的木板了。

    int c[1005];
    int ans[1005], beg[1005];
    
    void test_case() {
        int n, m, d;
        scanf("%d%d%d", &n, &m, &d);
        for(int i = 1; i <= m; ++i)
            scanf("%d", &c[i]);
    
        for(int i = m, p = n; i >= 1; --i) {
            for(int j = p, k = 1; k <= c[i]; --j, ++k) {
                ans[j] = i;
                beg[i] = j;
            }
            p -= c[i];
        }
    
        int curn = 0, curm = 0;
        while(1) {
            /*for(int i = 1; i <= n; ++i)
                printf("%d%c", ans[i], " 
    "[i == n]);*/
            if(curn + d > n || ans[curn + d]) {
                puts("YES");
                for(int i = 1; i <= n; ++i)
                    printf("%d%c", ans[i], " 
    "[i == n]);
                return;
            } else {
                ++curm;
                if(curm > m) {
                    puts("NO");
                    return;
                }
                for(int i = beg[curm], k = 1; k <= c[curm]; ++i, ++k)
                    ans[i] = 0;
                for(int i = curn + d, k = 1; k <= c[curm]; ++i, ++k) {
                    ans[i] = curm;
                    curn = i;
                }
            }
        }
    }
    

    D - Binary String Minimizing

    题意:给一个01串,使用不超过k次临位交换使得这个串最小。

    题解:明显是冒泡排序,感觉是优先把最前面的0冒到最头头。那么记录每个0的位置和头的位置就可以了。

    char s[1000005];
    int pos[1000005], ptop;
    
    void test_case() {
        int n;
        ll k;
        scanf("%d%lld%s", &n, &k, s + 1);
        ptop = 0;
        for(int i = 1; i <= n; ++i) {
            if(s[i] == '0')
                pos[++ptop] = i;
        }
        if(ptop == n) {
            puts(s + 1);
            return;
        }
        int first1 = 1, curp = 1;
        while(s[first1] == '0')
            ++first1, ++curp;
        while(k && curp <= ptop) {
            if(k >= pos[curp] - first1) {
                swap(s[pos[curp]], s[first1]);
                k -= pos[curp] - first1;
            } else {
                swap(s[pos[curp]], s[pos[curp] - k]);
                k = 0;
            }
            ++curp;
            ++first1;
        }
        puts(s + 1);
    }
    

    E - Yet Another Division Into Teams

    题意:给n个人,分成若干组,每组至少3个人,每组的极差就是该组的价值,求最小的总价值。

    题解:假如丢开数据大小来看,这个随便n^2的dp。

    dp[i]=min(dp[j]+a[i]-a[j+1]), for each j<=i-3

    移项,看起来就是:

    dp[i]=min(dp[j]-a[j+1])+a[i], for each j<=i-3

    区间RMQ,写个线段树。

    还得复原方案,得记录分割点是从哪里来的,真是麻烦。

    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 200000;
        //static const int INF = 0x3f3f3f3f;
        int mi[(MAXN << 2) + 5];
        int minid[(MAXN << 2) + 5];
    
        void PushUp(int o) {
            if(mi[ls] <= mi[rs]) {
                mi[o] = mi[ls];
                minid[o] = minid[ls];
            } else {
                mi[o] = mi[rs];
                minid[o] = minid[rs];
            }
        }
    
        void Build(int o, int l, int r) {
            if(l == r) {
                mi[o] = INF;
                minid[o] = l;
            } else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
            }
        }
    
        void Update(int o, int l, int r, int ql, int qr, int v) {
            if(ql <= l && r <= qr) {
                mi[o] = v;
            } else {
                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);
            }
        }
    
        pii QueryMin(int o, int l, int r, int ql, int qr) {
            if(ql > qr)
                return {INF, -1};
            if(ql <= l && r <= qr) {
                return {mi[o], minid[o]};
            } else {
                int m = l + r >> 1;
                pii res = {INF, -1};
                if(ql <= m) {
                    pii tmp = QueryMin(ls, l, m, ql, qr);
                    if(tmp.first < res.first)
                        res = tmp;
                }
                if(qr >= m + 1) {
                    pii tmp = QueryMin(rs, m + 1, r, ql, qr);
                    if(tmp.first < res.first)
                        res = tmp;
                }
                return res;
            }
        }
    #undef ls
    #undef rs
    } st;
    
    int n;
    pii a[200005];
    int dp[200005];
    int dp2[200005];
    
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i].first);
            a[i].second = i;
        }
        sort(a + 1, a + 1 + n);
        st.Build(1, 1, n);
        memset(dp, INF, sizeof(dp[0]) * (n + 1));
        dp[0] = 0;
        dp[3] = a[3].first - a[1].first;
        dp2[3] = 1;
        st.Update(1, 1, n, 3, 3, dp[3] - a[4].first);
        for(int i = 4; i <= n; ++i) {
            pii tmp = st.QueryMin(1, 1, n, 3, i - 3);
            dp[i] = tmp.first + a[i].first;
            dp2[i] = tmp.second + 1;
            if(dp[i - 1] - a[i - 1].first + a[i].first < dp[i]) {
                dp[i] = dp[i - 1] - a[i - 1].first + a[i].first;
                dp2[i] = dp2[i - 1];
            }
            st.Update(1, 1, n, i, i, dp[i] - a[i + 1].first);
        }
        printf("%d ", dp[n]);
        int cur = n, pre = dp2[n], cnt = 1;
        while(cur >= 1) {
            if(cur < pre) {
                ++cnt;
                pre = dp2[cur];
            }
            a[cur].first = cnt;
            --cur;
        }
        printf("%d
    ", cnt);
        for(int i = 1; i <= n; ++i)
            swap(a[i].first, a[i].second);
        sort(a + 1, a + 1 + n);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", a[i].second, " 
    "[i == n]);
    }
    

    再想想这个每次问的区间左端点都固定,改成map不就行了?

    map<int, int> st;
    
    int n;
    pii a[200005];
    int dp[200005];
    int dp2[200005];
    
    queue<pii>q;
    
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i].first);
            a[i].second = i;
        }
        sort(a + 1, a + 1 + n);
        memset(dp, INF, sizeof(dp[0]) * (n + 1));
        dp[0] = 0;
        dp[3] = a[3].first - a[1].first;
        dp2[3] = 1;
        q.push({dp[3] - a[4].first, 3});
        for(int i = 4; i <= n; ++i) {
            pii tmp = {INF, -1};
            if(st.size())
                tmp = *st.begin();
            dp[i] = tmp.first + a[i].first;
            dp2[i] = tmp.second + 1;
            if(dp[i - 1] - a[i - 1].first + a[i].first < dp[i]) {
                dp[i] = dp[i - 1] - a[i - 1].first + a[i].first;
                dp2[i] = dp2[i - 1];
            }
            q.push({dp[i] - a[i + 1].first, i});
            if(q.size() >= 3) {
                st[q.front().first] = q.front().second;
                q.pop();
            }
        }
        printf("%d ", dp[n]);
        int cur = n, pre = dp2[n], cnt = 1;
        while(cur >= 1) {
            if(cur < pre) {
                ++cnt;
                pre = dp2[cur];
            }
            a[cur].first = cnt;
            --cur;
        }
        printf("%d
    ", cnt);
        for(int i = 1; i <= n; ++i)
            swap(a[i].first, a[i].second);
        sort(a + 1, a + 1 + n);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", a[i].second, " 
    "[i == n]);
    }
    

    其实都只是取最小,不是单调栈就可以?

    int n;
    pii a[200005];
    int dp[200005];
    int dp2[200005];
    
    stack<pii>st;
    queue<pii>q;
    
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i].first);
            a[i].second = i;
        }
        sort(a + 1, a + 1 + n);
        memset(dp, INF, sizeof(dp[0]) * (n + 1));
        dp[0] = 0;
        dp[3] = a[3].first - a[1].first;
        dp2[3] = 1;
        q.push({dp[3] - a[4].first, 3});
        for(int i = 4; i <= n; ++i) {
            pii tmp = {INF, -1};
            if(st.size())
                tmp = st.top();
            dp[i] = tmp.first + a[i].first;
            dp2[i] = tmp.second + 1;
            if(dp[i - 1] - a[i - 1].first + a[i].first < dp[i]) {
                dp[i] = dp[i - 1] - a[i - 1].first + a[i].first;
                dp2[i] = dp2[i - 1];
            }
            q.push({dp[i] - a[i + 1].first, i});
            if(q.size() >= 3) {
                while(st.size() && st.top().first >= q.front().first)
                    st.pop();
                st.push(q.front());
                q.pop();
            }
        }
        printf("%d ", dp[n]);
        int cur = n, pre = dp2[n], cnt = 1;
        while(cur >= 1) {
            if(cur < pre) {
                ++cnt;
                pre = dp2[cur];
            }
            a[cur].first = cnt;
            --cur;
        }
        printf("%d
    ", cnt);
        for(int i = 1; i <= n; ++i)
            swap(a[i].first, a[i].second);
        sort(a + 1, a + 1 + n);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", a[i].second, " 
    "[i == n]);
    }
    

    不对,取前缀最小连单调栈都不需要。

    int n;
    pii a[200005];
    int dp[200005];
    int dp2[200005];
    
    pii st = {INF, INF};
    queue<pii>q;
    
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i].first);
            a[i].second = i;
        }
        sort(a + 1, a + 1 + n);
        memset(dp, INF, sizeof(dp[0]) * (n + 1));
        dp[0] = 0;
        dp[3] = a[3].first - a[1].first;
        dp2[3] = 1;
        q.push({dp[3] - a[4].first, 3});
        for(int i = 4; i <= n; ++i) {
            dp[i] = st.first + a[i].first;
            dp2[i] = st.second + 1;
            if(dp[i - 1] - a[i - 1].first + a[i].first < dp[i]) {
                dp[i] = dp[i - 1] - a[i - 1].first + a[i].first;
                dp2[i] = dp2[i - 1];
            }
            q.push({dp[i] - a[i + 1].first, i});
            if(q.size() >= 3) {
                if(q.front().first < st.first)
                    st = q.front();
                q.pop();
            }
        }
        printf("%d ", dp[n]);
        int cur = n, pre = dp2[n], cnt = 1;
        while(cur >= 1) {
            if(cur < pre) {
                ++cnt;
                pre = dp2[cur];
            }
            a[cur].first = cnt;
            --cur;
        }
        printf("%d
    ", cnt);
        for(int i = 1; i <= n; ++i)
            swap(a[i].first, a[i].second);
        sort(a + 1, a + 1 + n);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", a[i].second, " 
    "[i == n]);
    }
    

    总结:要先观察DP的式子,然后考虑数据结构的特点。取前缀的可以直接用一个值来维护,转移区间不断右移的可以用单调队列维护,只有当转移区间乱动的时候才需要线段树。在选择线段树的场合,注意维护最小值的坐标的求法。

    F - Equalizing Two Strings

    题意:有两个等长字符串s,t,每次选择一个长度,把s的这个长度的某区间和t的这个长度的某区间同时翻转,问能否使得s与t相等。

    题解:首先排序之后相同才有可能有解,那除此之外什么时候无解呢?设想每次翻转的区间长度都是2,变成一个临位交换,那么一次交换会使得逆序数变化1(冒泡排序),直觉感觉是长的翻转都是分解成这种临位交换的,那么s和t的逆序数会同时变化,所以只需要逆序数的差为偶数,那么可以让逆序数小的那个原地反复交换浪费掉差值。需要注意的是假如有某个字符出现了两次,那么这两个字符在排序之后就可以原地交换浪费掉差值。想不到1A了,说不定这些直觉和乱搞能力已经达到2200了,只是
    Graphsforces 要注意学图的知识。

    注意归并排序的时候,逆序数应该是longlong,而且只有在弹出j才增加逆序数为剩余的i的数量。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    char s[200005], t[200005], tmp[200005];
    
    ll mergesort(char *s, int l, int r) {
        if(l == r)
            return 0;
        int m = l + r >> 1;
        ll res = 0;
        res += mergesort(s, l, m);
        res += mergesort(s, m + 1, r);
        int i = l, j = m + 1, k = 0;
        while(i <= m || j <= r) {
            if(i > m)
                tmp[++k] = s[j++];
            else if(j > r)
                tmp[++k] = s[i++];
            else if(s[i] < s[j])
                tmp[++k] = s[i++];
            else {
                res += m - i + 1;
                tmp[++k] = s[j++];
            }
        }
        for(int i = 1; i <= k; ++i)
            s[l + i - 1] = tmp[i];
        return res;
    }
    
    void test_case() {
        int n;
        scanf("%d%s%s", &n, s + 1, t + 1);
        ll rs = mergesort(s, 1, n);
        ll rt = mergesort(t, 1, n);
        bool suc = 0;
        if(strcmp(s + 1, t + 1) == 0) {
            int tmp = unique(s + 1, s + 1 + n) - (s + 1);
            if(tmp != n || (rs - rt) % 2 == 0)
                suc = 1;
        }
        if(suc)
            puts("YES");
        else
            puts("NO");
    }
    
    int main() {
    #ifdef KisekiPurin
        freopen("KisekiPurin.in", "r", stdin);
    #endif // KisekiPurin
        int t = 1;
        scanf("%d", &t);
        for(int ti = 1; ti <= t; ++ti) {
            //printf("Case #%d: ", ti);
            test_case();
        }
    }
    
  • 相关阅读:
    Java学习笔记之---单例模型
    Java学习笔记之---内部类
    Java项目案例之--封装的实例
    Java学习笔记之---构造方法
    Java学习笔记之---static
    Java学习笔记之---面向对象
    咨询顾问与逻辑思客
    最重要与靠不住之间如何平衡
    《网络借贷信息中介机构业务活动管理暂行办法》
    商业银行法律法规与监管要求汇总
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/11891976.html
Copyright © 2011-2022 走看看