zoukankan      html  css  js  c++  java
  • Codeforces Round #592 (Div. 2)

    A - Pens and Pencils

    题意:有a堂作文课和b次练习课,每1支钢笔可以用c堂作文课,每1支铅笔可以用d堂练习课。求携带笔不超过k支的方案。

    题解:签到题,就要大大方方地签。不搞这么多绕来绕去的。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    void test_case() {
        int a, b, c, d, k;
        scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
        int ac = (a + (c - 1)) / c, ad = (b + (d - 1)) / d;
        if(ac + ad > k)
            puts("-1");
        else
            printf("%d %d
    ", ac, ad);
    }
    
    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();
        }
    }
    

    B - Rooms and Staircases

    题意:有两层楼的房间,[1,n]排一列,每次可以左右走,某些位置有楼梯。在不经过一个房间两次的前提下任选起点和路径,求最大经过的房间。

    题解:又签到,很明显是从一个尽头走到最远的楼梯上楼再回去,枚举两个尽头。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    char s[1005];
    
    void test_case() {
        int n;
        scanf("%d%s", &n, s + 1);
        int l = 1, r = n;
        while(l <= n) {
            if(s[l] == '0')
                ++l;
            else
                break;
        }
        while(r >= 1) {
            if(s[r] == '0')
                --r;
            else
                break;
        }
        if(l > r)
            printf("%d
    ", n);
        else
            printf("%d
    ", 2 * n - 2 * min(n - r, l - 1));
    }
    
    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();
        }
    }
    

    C - The Football Season

    题意:有n场球,一个队得了p分,已知赢一次得w分,平一次得d分,输不得分,且w>d。求一个解,无解输出-1。

    题解:不就是求xw+yd=p的解,然后要求x+y<=n吗?套个扩展欧几里得求出最小的非负x,显然最小的非负x不能再减小,而减小y必定使得x增大,由于w>d所以要使得得分保持不变y减少x增量会少一些。这就复杂了,反过来解这个方程,则xd+yw=p。

    注:这个题让我发现我模板里解线性同余方程会溢出longlong的bug,在不能使用int128的情况下很麻烦。通过一些分析我修复了这个问题,常数增大了一点,但是不容易溢出。具体详见模板。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    ll gcd(ll a, ll b) {
        if(b == 0)
            return a;
        while(ll t = a % b)
            a = b, b = t;
        return b;
    }
    
    ll ex_gcd(ll a, ll b, ll& x, ll& y) {
        if(b == 0) {
            x = 1, y = 0;
            return a;
        }
        ll d = ex_gcd(b, a % b, x, y), t;
        t = x, x = y, y = t - a / b * y;
        return d;
    }
    
    //解线性同余方程 ax + by = c ,无解返回false
    bool _LCE(ll a, ll b, ll c, ll &x0, ll &y0) {
        ll x, y, d = ex_gcd(a, b, x, y);
        if(c % d)
            return false;
        ll k = b / gcd(a, b);
        x0 = ((x % k) * (c / d % k) % k + k) % k;
        y0 = (c - a * x0) / b;
        //x0是x的最小非负整数解
        //x=x0+b*t,y=y0-a*t,是方程的所有解,对所有整数t成立
        return true;
    }
    
    //解线性同余方程 ax = b mod n ,无解返回false
    //和方程 ax + ny = b 等价
    bool LCE(ll a, ll b, ll n, ll &x0) {
        ll x, y;
        if(_LCE(a, n, b, x, y)) {
            ll k = n / gcd(a, n);
            x0 = (x % k + k) % k;
            //x0是最小非负整数解
            //x=x0+k*t,是方程的所有解,对所有整数t成立
            return true;
        } else
            return false;
    }
    
    void test_case() {
        ll n, p, w, d, x, y, z = -1;
        scanf("%lld%lld%lld%lld", &n, &p, &w, &d);
        if(_LCE(d, w, p, y, x))
            z = n - x - y;
        if(x >= 0 && z >= 0)
            printf("%lld %lld %lld
    ", x, y, z);
        else
            puts("-1");
    }
    
    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();
        }
    }
    

    但是这个太草了,其实只需要枚举一个数,另一个数直接除下去就可以。

    void test_case() {
        ll n, p, w, d;
        scanf("%lld%lld%lld%lld", &n, &p, &w, &d);
        for(ll x = 0; x * w <= p; ++x) {
            if((p - x * w) % d)
                continue;
            ll y = (p - x * w) / d;
            ll z = n - x - y;
            if(z >= 0) {
                printf("%lld %lld %lld", x, y, z);
                return;
            }
        }
        puts("-1");
        return;
    }
    

    这样会T,都不看范围的?其实是经过严密的观察发现,w个d和d个w是等价的,而把w个d换成d个w可以节省n,多出来的丢给z,所以y只有[0,m-1]这样的取值。还是封装一个带mod的大数比较靠谱。

    void test_case() {
        ll n, p, w, d;
        scanf("%lld%lld%lld%lld", &n, &p, &w, &d);
        for(ll y = 0; y < w && y * d <= p; ++y) {
            if((p - y * d) % w)
                continue;
            ll x = (p - y * d) / w;
            ll z = n - x - y;
            if(z >= 0) {
                printf("%lld %lld %lld", x, y, z);
                return;
            }
        }
        puts("-1");
        return;
    }
    

    D - Paint the Tree

    树形dp?求每个点染成某色的最小代价?但是还和孙子节点有关,想到这里的时候就发现dp要记录孙子的两个颜色,假如孙子有两个颜色那么儿子的颜色就确定了,但是这个点本身就没办法涂了,所以孙子只能有1种颜色。

    题意:给一棵树染3种色其中之一,使得任意一条长为2的简单路径上的3个点的颜色都不同。这样就要求树没有分叉,否则有一个点度为3,那么不可能涂色完成。那么就是给一条链涂色,随便dp一下。

    注意一定要分清楚换名之后的id和原id,以及现在在操作哪个id。

    int a[100005][3];
    int deg[100005];
    int id[100005];
    int aid[100005];
    vector<int> G[100005];
    ll dp[100005][3][3];
    
    int cnt = 0;
    void dfs(int u, int p) {
        id[u] = ++cnt;
        aid[cnt] = u;
        for(auto &v : G[u]) {
            if(v == p)
                continue;
            dfs(v, u);
        }
    }
    
    int op(int i, int j) {
        if(i == 0)
            return j == 1 ? 2 : 1;
        if(i == 1)
            return j == 0 ? 2 : 0;
        return j == 0 ? 1 : 0;
    }
    
    int ans[100005];
    
    void test_case() {
        int n;
        scanf("%d", &n);
        rep(i, 1, n) scanf("%d", &a[i][0]);
        rep(i, 1, n) scanf("%d", &a[i][1]);
        rep(i, 1, n) scanf("%d", &a[i][2]);
        rep(i, 1, n - 1) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
            ++deg[u], ++deg[v];
            if(deg[u] >= 3 || deg[v] >= 3) {
                puts("-1");
                return;
            }
        }
    
        int root = -1;
        rep(i, 1, n) {
            if(deg[i] == 1) {
                root = i;
                break;
            }
        }
        dfs(root, -1);
        memset(dp, INF, sizeof(dp));
        for(int i = 0; i <= 2; ++i) {
            for(int j = 0; j <= 2; ++j) {
                if(i == j)
                    continue;
                dp[2][i][j] = a[aid[1]][i] + a[aid[2]][j];
            }
        }
        rep(k, 3, n) {
            for(int i = 0; i <= 2; ++i) {
                for(int j = 0; j <= 2; ++j) {
                    if(i == j)
                        continue;
                    dp[k][i][j] = dp[k - 1][op(i, j)][i] + a[aid[k]][j];
                }
            }
        }
        ll ANS = 1ll * INF * INF;
        int c1, c2;
        for(int i = 0; i <= 2; ++i) {
            for(int j = 0; j <= 2; ++j) {
                if(i == j)
                    continue;
                if(dp[n][i][j] < ANS) {
                    ANS = dp[n][i][j];
                    c1 = j;
                    c2 = i;
                }
            }
        }
        printf("%lld
    ", ANS);
        ans[n] = c1;
        ans[n - 1] = c2;
        per(i, n - 2, 1) ans[i] = op(ans[i + 1], ans[i + 2]);
        rep(i, 1, n) printf("%d%c", ans[id[i]] + 1, " 
    "[i == n]);
        return;
    }
    

    E - Minimizing Difference

    题意:给一个序列,每次操作可以选一个元素+1或者选一个元素-1,求不超过k次操作后生成的最小差。

    题解:数据小的话可以贪心,每次选最大和最小里面出现次数较少的那个集体变化。这样每次操作会合并至少一个元素或者把k消耗到几乎殆尽。假如我二分一个极差k,然后尺取移动这个极差就可以转移出需要的值,所以二分+尺取是对的,先写这个解。

    朴素的二分+尺取没有办法解决最后的边界不出现在ai中的情形。

    作出一个假设,最优解的其中一种构造必定以ai为其中之一边界。这个结论看起来就很显然!得到这个结论之后需要进行正反两次check。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    #define rep(i,p1,p2) for(int i=(p1);i<=(p2);++i)
    #define per(i,p1,p2) for(int i=(p1);i>=(p2);--i)
    
    const int INF = 0x3f3f3f3f;
    const int MOD = 1e9 + 7;
    
    int qpow(ll x, ll n) {
        ll res = 1;
        while(n) {
            if(n & 1)
                res = res * x % MOD;
            x = x * x % MOD;
            n >>= 1;
        }
        return res;
    }
    
    const int MAXN = 2e5;
    
    /*---*/
    
    int n;
    ll k;
    int a[100005];
    ll prefix[100005];
    ll suffix[100005];
    int a2[100005];
    ll prefix2[100005];
    ll suffix2[100005];
    
    bool check(ll d) {
        ll cur = 0;
        int i = 1, j = 1;
        while(j + 1 <= n && a[j + 1] - a[i] <= d)
            ++j;
        cur = prefix[i] + suffix[j + 1];
        if(j + 1 <= n)
            cur += (n - j) * (a[j + 1] - (a[i] + d));
        if(cur <= k)
            return true;
        while(j < n) {
            ++i;
            while(j + 1 <= n && a[j + 1] - a[i] <= d)
                ++j;
            cur = prefix[i] + suffix[j + 1];
            if(j + 1 <= n)
                cur += (n - j) * (a[j + 1] - (a[i] + d));
            if(cur <= k)
                return true;
        }
        cur = 0;
        i = 1, j = 1;
        while(j + 1 <= n && a2[i] - a2[j + 1] <= d)
            ++j;
        cur = prefix2[i] + suffix2[j + 1];
        if(j + 1 <= n)
            cur += (n - j) * ((a2[i] - d) - a2[j + 1]);
        if(cur <= k)
            return true;
        while(j < n) {
            ++i;
            while(j + 1 <= n && a2[i] - a2[j + 1] <= d)
                ++j;
            cur = prefix2[i] + suffix2[j + 1];
            if(j + 1 <= n)
                cur += (n - j) * ((a2[i] - d) - a2[j + 1]);
            if(cur <= k)
                return true;
        }
        return false;
    }
    
    ll bs() {
        ll L = 0, R = a[n] - a[1];
        while(1) {
            ll M = (L + R) >> 1;
            if(L == M) {
                if(check(L))
                    return L;
                return R;
            }
            if(check(M))
                R = M;
            else
                L = M + 1;
        }
    }
    
    void test_case() {
        scanf("%d%lld", &n, &k);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        sort(a + 1, a + 1 + n);
        for(int i = 1; i <= n; ++i)
            prefix[i] = prefix[i - 1] + 1ll * (i - 1) * (a[i] - a[i - 1]);
        for(int i = n; i >= 1; --i)
            suffix[i] = suffix[i + 1] + 1ll * (n - i) * (a[i + 1] - a[i]);
        for(int i = 1; i <= n; ++i)
            a2[i] = a[n - i + 1];
        for(int i = 1; i <= n; ++i)
            prefix2[i] = prefix2[i - 1] + 1ll * (i - 1) * (a2[i - 1] - a2[i]);
        for(int i = n; i >= 1; --i)
            suffix2[i] = suffix2[i + 1] + 1ll * (n - i) * (a2[i] - a2[i + 1]);
        printf("%lld
    ", bs());
    }
    
    /*---*/
    
    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();
        }
    }
    
    /*
        1. 小数据问题退化:
            输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
        2. 内存:
            内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
            可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?字符大小写有考虑吗?
            多组数据有进行初始化吗?memset会不会翻车?
        3. 算术溢出:
            乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
        4. 习惯:
            函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
            Yes和No有没有反,大小写有没有错?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
            id和aid是不是没有考虑,是不是弄反了?操作的是换名之后的,输出时应该是换名之前的。
        5. 其他bug:
            基环树是树加环,不是链加环。
    */
    

    的确直接贪心推进边界是最简单的,每次移动要么合并多一个元素,要么把k消去一大截,使得不再能推进这个边界。

    int n;
    ll k;
    int a[100005];
    
    void test_case() {
        scanf("%d%lld", &n, &k);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
        }
        sort(a + 1, a + 1 + n);
        int L = 1, R = n;
        int cntL = 1, cntR = 1;
        int curL = a[1], curR = a[n];
        while(L + 1 <= R && a[L + 1] == curL) {
            ++cntL;
            ++L;
        }
        while(R - 1 >= L && a[R - 1] == curR) {
            ++cntR;
            --R;
        }
        if(L == R) {
            puts("0");
            return;
        }
        while(L < R && k >= min(cntL, cntR)) {
            if(cntL <= cntR) {
                int d = min(k / cntL, 1ll * a[L + 1] - a[L]);
                curL += d;
                k -= 1ll * d * cntL;
                while(L + 1 <= R && a[L + 1] == curL) {
                    ++cntL;
                    ++L;
                }
            } else {
                int d = min(k / cntR, 1ll * a[R] - a[R - 1]);
                curR -= d;
                k -= 1ll * d * cntR;
                while(R - 1 >= L && a[R - 1] == curR) {
                    ++cntR;
                    --R;
                }
            }
        }
        printf("%d
    ", curR - curL);
    }
    

    F - Chips

    题意:围成一个环的n颗黑白球,每个球每个时刻后会发生变化:当且仅当其邻居都和它异色时自己会变色。所有的球是同时变色的。求k时刻后这个环的样子。

    题解:假如是偶数个球且黑白间隔,那么只需要判断k的奇偶性就可以了。否则连续的两个以上同色球是不会变化的,会变化的只是若干条链,我称为“异色链”,比如:

    BWBWBWB
    

    异色链就是

    WBWBW
    

    注意到每时刻异色链的长度会缩短2。直接贪心。

    不知道这样写对不对:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    #define rep(i,p1,p2) for(int i=(p1);i<=(p2);++i)
    #define per(i,p1,p2) for(int i=(p1);i>=(p2);--i)
    
    const int INF = 0x3f3f3f3f;
    const int MOD = 1e9 + 7;
    
    int qpow(ll x, ll n) {
        ll res = 1;
        while(n) {
            if(n & 1)
                res = res * x % MOD;
            x = x * x % MOD;
            n >>= 1;
        }
        return res;
    }
    
    const int MAXN = 2e5;
    
    /*---*/
    
    int n, k;
    char a[600005];
    char b[600005];
    
    void test_case() {
        scanf("%d%lld%s", &n, &k, a + 1);
        bool suc = 1;
        for(int i = 2; i <= n; ++i) {
            if(a[i] != a[i - 1]) {
                suc = 0;
                break;
            }
        }
        if(suc) {
            puts(a + 1);
            return;
        }
        if(!(n & 1)) {
            bool suc = 1;
            for(int i = 2; i <= n; ++i) {
                if(a[i] == a[i - 1]) {
                    suc = 0;
                    break;
                }
            }
            if(suc) {
                if(k & 1) {
                    for(int i = 1; i <= n; ++i)
                        printf("%c", "BW"[a[i] == 'B']);
                    putchar('
    ');
                    return;
                }
                for(int i = 1; i <= n; ++i)
                    printf("%c", "BW"[a[i] == 'W']);
                putchar('
    ');
                return;
            }
        }
        for(int i = 1; i <= n; ++i)
            a[i + n + n] = a[i + n] = a[i];
        int L, beg;
        for(int i = 2;; ++i) {
            if(a[i] == a[i - 1]) {
                L = i - 1;
                beg = i - 1;
                break;
            }
        }
        int R = L + n - 1;
        //printf("L=%d R=%d
    ", L - beg + 1, R - beg + 1);
        while(a[R] == a[L])
            --R;
        while(a[L + 1] == a[L])
            ++L;
        ++L;
        /*此时[L,R]就是链的两端*/
        while(L <= R) {
            //printf("L=%d R=%d
    ", L - beg + 1, R - beg + 1);
            int M = L;
            for(int i = L + 1; i <= R; ++i) {
                if(a[i] != a[i - 1])
                    M = i;
                else {
                    M = i - 2;
                    break;
                }
            }
            if(M >= L) {
                /*此时[L,M]为第一个异色链*/
                if((M - L + 1) % 2 == 0) {
                    //printf("L=%d M=%d
    ", L - beg + 1, M - beg + 1);
                    //偶数
                    int d = min(k, (M - L + 1) / 2);
                    int L2 = L, M2 = M;;
                    for(int i = L, u = 1; u <= d; ++u, ++i) {
                        a[i] = a[i - 1];
                        L2 = i + 1;
                    }
                    for(int i = M, u = 1; u <= d; ++u, --i) {
                        a[i] = a[i + 1];
                        M2 = i - 1;
                    }
                    if(k & 1) {
                        for(int i = L2; i <= M2; ++i)
                            a[i] = ("BW"[a[i] == 'B']);
                    }
                } else {
                    //printf("L=%d M=%d
    ", L - beg + 1, M - beg + 1);
                    int d = min(k, (M - L + 1 + 1) / 2);
                    int L2 = L, M2 = M;;
                    for(int i = L, u = 1; u <= d; ++u, ++i) {
                        a[i] = a[i - 1];
                        L2 = i + 1;
                    }
                    for(int i = M, u = 1; u <= d; ++u, --i) {
                        a[i] = a[i + 1];
                        M2 = i - 1;
                    }
                    if(k & 1) {
                        for(int i = L2; i <= M2; ++i)
                            a[i] = ("BW"[a[i] == 'B']);
                    }
                }
                L = M + 1;
            } else {
                ++L;
            }
            while(L + 1 <= R && a[L + 1] == a[L])
                ++L;
            ++L;
            /*for(int i = beg, u = 1; u <= n; ++u, ++i)
                putchar(a[i]);
            putchar('
    ');*/
        }
        for(int i = beg, u = 1; u <= n; ++u, ++i) {
            b[i] = a[i];
            if(i - n >= 1)
                b[i - n] = a[i];
        }
        for(int i = 1; i <= n; ++i)
            putchar(b[i]);
        putchar('
    ');
    }
    
    /*---*/
    
    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();
        }
    }
    
    /*
        1. 小数据问题退化:
            输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
        2. 内存:
            内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
            可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?字符大小写有考虑吗?
            多组数据有进行初始化吗?memset会不会翻车?
        3. 算术溢出:
            乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
        4. 习惯:
            函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
            Yes和No有没有反,大小写有没有错?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
            id和aid是不是没有考虑,是不是弄反了?操作的是换名之后的,输出时应该是换名之前的。
        5. 其他bug:
            基环树是树加环,不是链加环。
    */
    
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    #define rep(i,p1,p2) for(int i=(p1);i<=(p2);++i)
    #define per(i,p1,p2) for(int i=(p1);i>=(p2);--i)
    
    const int INF = 0x3f3f3f3f;
    const int MOD = 1e9 + 7;
    
    int qpow(ll x, ll n) {
        ll res = 1;
        while(n) {
            if(n & 1)
                res = res * x % MOD;
            x = x * x % MOD;
            n >>= 1;
        }
        return res;
    }
    
    const int MAXN = 2e5;
    
    /*---*/
    
    int n, k;
    char a[400005];
    
    void test_case() {
        scanf("%d%d%s", &n, &k, a + 1);
        bool suc = 1;
        for(int i = 2; i <= n; ++i) {
            if(a[i] != a[i - 1]) {
                suc = 0;
                break;
            }
        }
        if(suc) {
            puts(a + 1);
            return;
        }
        if(n % 2 == 0) {
            bool suc = 1;
            for(int i = 2; i <= n; ++i) {
                if(a[i] == a[i - 1]) {
                    suc = 0;
                    break;
                }
            }
            if(suc) {
                if(k & 1) {
                    for(int i = 1; i <= n; ++i)
                        printf("%c", "BW"[a[i] == 'B']);
                    putchar('
    ');
                    return;
                }
                for(int i = 1; i <= n; ++i)
                    printf("%c", "BW"[a[i] == 'W']);
                putchar('
    ');
                return;
            }
        }
        for(int i = 1; i <= n; ++i)
            a[i + n] = a[i];
        int L, beg;
        for(int i = 2;; ++i) {
            if(a[i] == a[i - 1]) {
                L = i - 1;
                beg = i - 1;
                break;
            }
        }
        int R = L + n - 1;
        while(a[R] == a[L])
            --R;
        while(a[L + 1] == a[L])
            ++L;
        ++L;
        /*此时[L,R]就是链的两端*/
        while(L <= R) {
            int M = R;
            for(int i = L + 1; i <= R; ++i) {
                if(a[i] == a[i - 1]) {
                    M = i - 2;
                    break;
                }
            }
            if(M >= L) {
                /*此时[L,M]为第一个异色链*/
                int d;
                if((M - L + 1) % 2 == 0)
                    d = min(k, (M - L + 1) / 2);
                else
                    d = min(k, (M - L + 1 + 1) / 2);
                int L2 = L, M2 = M;;
                for(int i = L, u = 1; u <= d; ++u, ++i) {
                    a[i] = a[i - 1];
                    L2 = i + 1;
                }
                for(int i = M, u = 1; u <= d; ++u, --i) {
                    a[i] = a[i + 1];
                    M2 = i - 1;
                }
                if(k & 1) {
                    for(int i = L2; i <= M2; ++i)
                        a[i] = ("BW"[a[i] == 'B']);
                }
                L = M + 1;
            } else {
                /*此时没有异色链*/
                ++L;
            }
            while(L + 1 <= R && a[L + 1] == a[L])
                ++L;
            ++L;
        }
        for(int i = beg, u = 1; u <= n; ++u, ++i) {
            if(i - n >= 1)
                a[i - n] = a[i];
        }
        for(int i = 1; i <= n; ++i)
            putchar(a[i]);
        putchar('
    ');
    }
    
    /*---*/
    
    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();
        }
    }
    
    /*
        1. 小数据问题退化:
            输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
        2. 内存:
            内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
            可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?字符大小写有考虑吗?
            多组数据有进行初始化吗?memset会不会翻车?
        3. 算术溢出:
            乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
        4. 习惯:
            函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
            Yes和No有没有反,大小写有没有错?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
            id和aid是不是没有考虑,是不是弄反了?操作的是换名之后的,输出时应该是换名之前的。
        5. 其他bug:
            基环树是树加环,不是链加环。
    */
    

    G - Running in Pairs

    题意:给两个排列,调整他们的位置使得他们对应位置的最大值的和最大,且这个值不能超过k。

    题解:最小的肯定是同向,最大的肯定是反向。但是怎么调整呢?一开始全部设为同向,然后发现首尾对称位置交换会使得答案变大,且能最节约这个最大的数字,当序列长度为奇数时,每次交换得到都是偶数,且这个收益逐渐减少到2,最后可能可以进行一次临位交换有可能把答案扩大1(仅当本身中部是正序时)。当序列的长度为偶数时,首尾交换每次都是奇数,且收益减少到1,显然这样可以凑出任意一个自然数。(错,不能构造出2,估计很多人也要FST在这里了,2要通过其他方法构造出来)

    1 2 3 4
    1 2 3 4
    

    偶数个的情况,截取中间的四个如上,因为外面已经处理过+5了,所以现在只需要考虑<=4的情况。
    要构造4,和上面说的一样,+3+1:swap(1,4),swap(2,3)
    要构造3,和上面说的一样,+3:swap(1,4)
    要构造2,+2:swap(2,4)
    要构造1,和上面说的一样,+1:swap(2,3)

    大的思路是对的,细节不行,以后在边界(这里的边界是中部数据较小时)要格外注意。

    int n;
    ll k;
    
    int a[1000005];
    int b[1000005];
    
    void test_case() {
        scanf("%d%lld", &n, &k);
        if(n == 1) {
            printf("1
    1
    1
    ");
            return;
        }
        if(n == 2) {
            if(k < 3)
                printf("-1
    ");
            else if(k == 3)
                printf("3
    1 2
    1 2
    ");
            else
                printf("4
    1 2
    2 1
    ");
            return;
        }
        ll sum = 0;
        for(int i = 1; i <= n; ++i) {
            a[i] = i;
            b[i] = i;
            sum += i;
        }
        if(k < sum) {
            puts("-1");
            return;
        }
    
        int mid = (n + 1) / 2;
        if(n % 2 == 1) {
            for(int i = 1; i <= n / 2; ++i) {
                ll d = (a[n - i + 1] - a[i]);
                if(sum + d <= k) {
                    swap(b[i], b[n - i + 1]);
                    sum += d;
                }
            }
            if(sum + 1 <= k) {
                if(b[mid - 1] < b[mid]) {
                    swap(a[mid - 1], a[mid]);
                    ++sum;
                }
            }
        } else {
            for(int i = 1; i <= (n - 4) / 2; ++i) {
                ll d = (a[n - i + 1] - a[i]);
                if(sum + d <= k) {
                    swap(b[i], b[n - i + 1]);
                    sum += d;
                }
            }
            if(sum + 4 <= k) {
                swap(b[mid], b[mid + 1]);
                swap(b[mid - 1], b[mid + 2]);
                sum += 4;
            } else if(sum + 3 <= k) {
                swap(b[mid - 1], b[mid + 2]);
                sum += 3;
            } else if(sum + 2 <= k) {
                swap(b[mid], b[mid + 2]);
                sum += 2;
            } else if(sum + 1 <= k) {
                swap(b[mid], b[mid + 1]);
                sum += 1;
            }
        }
        ll tsum = 0;
        for(int i = 1; i <= n; ++i)
            tsum += max(a[i], b[i]);
        printf("%lld
    ", tsum);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", a[i], " 
    "[i == n]);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", b[i], " 
    "[i == n]);
        assert(tsum == sum);
    }
    
  • 相关阅读:
    Dapper 基础用法
    测试的分类
    Python
    MySQL数据库的增删改查
    Python面向对象之
    Python面向对象之
    Python
    HTML5/CSS3/JS笔记
    Python-Flask框架之
    Python进程与线程
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/11885503.html
Copyright © 2011-2022 走看看