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

    A - Changing Volume

    题意:有个遥控器,有+1-1+2-2+5-5,6个键,不允许把音量调整至负数(当音量<5,则无法按-5),求最少按键使得a变成b。

    题解:一开始以为音量<5按-5会变成0,这样要多一个特判。由于给的数字的缘故,并不会有穿过去然后反过来减掉更优的,比如+4不需要+5-1,直接+2+2。所以直接模拟。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    void test_case() {
        int a, b;
        scanf("%d%d", &a, &b);
        if(a > b)
            swap(a, b);
        printf("%d
    ", (b - a) / 5 + ((b - a) % 5 + 1) / 2);
    }
    
    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 - Fridge Lockers

    题意:有个n点m边无向图,每个点是冰箱,每条边是一种锁,要求安排一种方法使得每个点的锁不被另一个点的锁覆盖。

    题解:原本m可以>n,这题就很复杂了,应该就把多出来的边换掉一最昂贵的边,意思是取出目前最贵的一组(u,v),然后连边(x,u),(x,v),这样就消费掉1条边且只增加了2x,当x取最小值时,比每次取出最小的(x,y)增加的x+y要便宜。但是这样贪心是不是有漏洞就不得而知了。现在规定m<=n,很显然不被另一个锁覆盖的意思就是要连至少两条边,且两条边连去不同的点。这样有解的时候当n>2且m=n时,方法是连成一个环。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    void test_case() {
        int n, m;
        scanf("%d%d", &n, &m);
        bool suc = (m == n && n > 2);
        int sum = 0;
        for(int i = 1, x; i <= n; ++i) {
            scanf("%d", &x);
            sum += x;
        }
        if(!suc) {
            puts("-1");
            return;
        }
        printf("%d
    ", 2 * sum);
        for(int i = 1; i <= n - 1; ++i)
            printf("%d %d
    ", i, i + 1);
        printf("%d %d
    ", n, 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 - League of Leesins

    题意:一个n个数的序列,分成n-2个连续的三元组,三元组之间的顺序可调,三元组内部的顺序也可调。要求复原其中一种。保证有解。

    题解:一头一尾肯定只出现1次,然后次头次尾只出现2次(当n>=4),安排好这两个之后,就一路确定下去了。但是怎么实现呢?考虑用一个vector存一个数的左右最多4个邻居,然后每次固定下来的数把邻居给删掉(置为-1),要注意修改其中元素的写法是for(auto &i: vec),又学了一招新的。vec是真的方便。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    int ans[100005], cnt[100005];
    vector<int> nei[100005];
     
    void test_case() {
        int n;
        scanf("%d", &n);
        memset(cnt, 0, sizeof(cnt[0]) * (n + 1));
        for(int i = 1; i <= n; ++i)
            nei[i].clear();
        for(int i = 1; i <= n - 2; ++i) {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            ++cnt[x], ++cnt[y], ++cnt[z];
            nei[x].push_back(y);
            nei[x].push_back(z);
            nei[y].push_back(x);
            nei[y].push_back(z);
            nei[z].push_back(x);
            nei[z].push_back(y);
        }
        for(int i = 1; i <= n; ++i) {
            sort(nei[i].begin(), nei[i].end());
            nei[i].resize(unique(nei[i].begin(), nei[i].end()) - nei[i].begin());
        }
        int h1 = -1;
        int ch1 = -1, ch2 = -1;
        for(int i = 1; i <= n; ++i) {
            if(cnt[i] == 1)
                h1 = i;
            if(cnt[i] == 2) {
                if(ch1 == -1)
                    ch1 = i;
                else
                    ch2 = i;
            }
        }
        if(nei[h1][0] != ch1 && nei[h1][1] != ch1)
            swap(ch1, ch2);
        int nh1 = -1;
        for(int i = 1; i <= n - 1; ++i) {
            ans[i] = h1;
            ans[i + 1] = ch1;
            for(auto j : nei[h1]) {
                if(j != -1 && j != ch1) {
                    nh1 = j;
                    break;
                }
            }
            for(auto &j : nei[ch1]) {
                if(j == h1)
                    j = -1;
            }
            for(auto &j : nei[nh1]) {
                if(j == h1)
                    j = -1;
            }
            h1 = ch1;
            ch1 = nh1;
        }
        for(int i = 1; i <= n; ++i)
            printf("%d%c", ans[i], " 
    "[i == n]);
    }
     
    int main() {
    #ifdef KisekiPurin
        freopen("KisekiPurin.in", "r", stdin);
    #endif // KisekiPurin
        int t = 1;
        for(int ti = 1; ti <= t; ++ti) {
            //printf("Case #%d: ", ti);
            test_case();
        }
    }
    

    D - Feeding Chicken

    题意:有一个格子农场,上面有一些格子是R表示有食物,有k只鸡,保证食物一定比鸡多,求一种分法使得食物尽可能平均,且每只鸡分到的地是连通的。平均是指极差最小。

    题解:一个格子农场必定可以被一条往返扫的线(或者一条螺旋形的线)覆盖,其中线的任意一段都是连通的。然后贪心,每次分够当前鸡需要的食物就截断。要注意每个格子都要分给某只鸡,所以剩下的格子要分给最后一只鸡(不要使用memset)。输入的时候要注意写法,不是扫进g+1而是g[i]+1。要平均分那肯定m1取K/k的下整,m2取m1+1,然后m2的数量恰好就是K%k剩下的数量。左右扫描线的写法偷学了大神的写法,就按照i的奇偶性判断j的方向,然后j越界的瞬间阻止其越界并增加i(之前有个蛇梯棋的期望dp但是当时是给每个点赋值一个编号,这个太蠢了)。给字符编码还有一种偷懒的直接用if(isalnum(i))来搞。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    char id[67];
    char g[105][105], h[105][105];
    
    void test_case() {
        int r, c, k;
        scanf("%d%d%d", &r, &c, &k);
        for(int i = 1; i <= r; ++i)
            scanf("%s", g[i] + 1);
        int K = 0;
        for(int i = 1; i <= r; ++i) {
            for(int j = 1; j <= c; ++j) {
                if(g[i][j] == 'R')
                    ++K;
            }
        }
        int m1 = K / k;
        int m2 = m1 + 1;
        int cntm2 = K % k;
        int cntm1 = k - cntm2;
    
        for(int i = 1; i <= r; ++i) {
            for(int j = 1; j <= c; ++j)
                h[i][j] = id[k];
        }
    
        int i = 1, j = 1, cntid = 0;
        while(cntm1--) {
            ++cntid;
            int rest = m1;
            while(rest) {
                h[i][j] = id[cntid];
                if(g[i][j] == 'R')
                    --rest;
                if(i & 1) {
                    ++j;
                    if(j > c) {
                        j = c;
                        ++i;
                    }
                } else {
                    --j;
                    if(j <= 0) {
                        j = 1;
                        ++i;
                    }
                }
            }
        }
    
        while(cntm2--) {
            ++cntid;
            int rest = m2;
            while(rest) {
                h[i][j] = id[cntid];
                if(g[i][j] == 'R')
                    --rest;
                if(i & 1) {
                    ++j;
                    if(j > c) {
                        j = c;
                        ++i;
                    }
                } else {
                    --j;
                    if(j <= 0) {
                        j = 1;
                        ++i;
                    }
                }
            }
        }
    
        for(int i = 1; i <= r; ++i) {
            for(int j = 1; j <= c; ++j)
                printf("%c", h[i][j]);
            printf("
    ");
        }
    }
    
    int main() {
    #ifdef KisekiPurin
        freopen("KisekiPurin.in", "r", stdin);
    #endif // KisekiPurin
        int top = 0;
        for(char i = '0'; i <= '9'; ++i)
            id[++top] = i;
        for(char i = 'A'; i <= 'Z'; ++i)
            id[++top] = i;
        for(char i = 'a'; i <= 'z'; ++i)
            id[++top] = i;
        int t = 1;
        scanf("%d", &t);
        for(int ti = 1; ti <= t; ++ti) {
            //printf("Case #%d: ", ti);
            test_case();
        }
    }
    

    E1 - Send Boxes to Alice (Easy Version)

    见E2

    E2 - Send Boxes to Alice (Hard Version)

    题意:有一列n个盒子,每个盒子有ai>=0个礼物,花费1代价可以把一个礼物移动到相邻的格子,计算最小的代价使得每个盒子的礼物数都被某个大于1的数整除。

    题解:注意到所有的数都要被某个数k整除,那么一定是k的倍数。全部加起来也是k的倍数,从sum的因数中找,而假如小的因数满足就没必要验证大的,就只需要验证sum的质因数种类(至多十几个)。但是怎么找最小呢?

    画了一下貌似全部取x的余数之后,一直累加余数的和过去就行了,但是过不了第一个样例,因为其实是可以向左移动的。改成min(tmp,x-tmp)就可以了。

    为什么呢?要把这个位置变成x的倍数,要么向上变要么向下变。向上变的话就把多出来的余数丢给下一个数就可以了。向下变的话要从下一个数中接收,可以提前预支下一个数补过来的花费,也是把余数丢给下一个数。为什么下一个数一定可以补呢?因为它也可以向再下一个数借。

    int n;
    int a[1000005];
    ll factor[205];
    int ftop;
    
    ll check(ll x, ll cur) {
        ll tmp = 0, sum = 0;
        for(int i = 1; i <= n; ++i) {
            tmp = (tmp + a[i]) % x;
            sum += min(tmp, x - tmp);
            if(sum >= cur)
                return sum;
        }
        return sum;
    }
    
    ll bs() {
        ll minans = 1ll * INF * INF;
        for(int i = 1; i <= ftop; ++i)
            minans = min(minans, check(factor[i], minans));
        return minans;
    }
    
    void test_case() {
        scanf("%d", &n);
        ll sum = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            sum += a[i];
        }
        if(sum == 1) {
            puts("-1");
            return;
        }
        ll tsum = sum;
        for(ll i = 2; i * i <= tsum; ++i) {
            if(tsum % i == 0) {
                factor[++ftop] = i;
                while(tsum % i == 0)
                    tsum /= i;
            }
        }
        if(tsum > 1)
            factor[++ftop] = tsum;
        random_shuffle(factor + 1, factor + 1 + ftop);
        printf("%lld
    ", bs());
    }
    

    打个random看看会不会快点,话说random_shuffle会收srand影响吗?

  • 相关阅读:
    xtrabackup备份原理
    四“当”
    MySQL BinLog Server 搭建实战
    mysqldump 原理
    MGR测试及搭建
    自动化测试-12.selenium的弹出框处理
    自动化测试-11.selenium的下拉框处理类Select
    自动化测试-10.selenium的iframe与Frame
    自动化测试-9.selenium多窗口句柄的切换
    自动化测试-8.selenium操作元素之键盘和鼠标事件
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/11896551.html
Copyright © 2011-2022 走看看