Codeforces Round #593 (Div. 2)

A - Stones

题意:有3堆石头a个,b个,c个,可以每次拿1个a堆的,2个b堆的,或者拿1个b堆的,2个c堆的。

题解:b堆是唯一的公共资源,考虑尽可能充分利用b堆。每2个b堆的参与操作2可以得6个,参与操作1只能得3个,而且参与操作2只需要b堆还剩下一个就可以执行,所以先做操作2。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t;
    scanf("%d", &t);
    while(t--) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        int n1 = min(b, c / 2);
        b -= n1;
        int n2 = min(a, b / 2);
        printf("%d\n", 3 * (n1 + n2));
    }
    return 0;
}

B - Alice and the List of Presents

题意:有n种礼物,m个盒子。每个盒子每种礼物个至多一个。每种礼物总共至少放一个。求方案数。

题解:把每种礼物不放的那种去除。每个盒子放和不放有两种,每种礼物有2^m-1种,全部乘起来。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int mod = 1e9 + 7;

ll pow_mod(ll x, int n) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int n, m;
    scanf("%d%d", &n, &m);
    printf("%lld\n", pow_mod(pow_mod(2, m) - 1, n));
}

C - Labs

题意:有n*n个数,均分成n组,每个大数向小数连一条流量为1的边。组X与组Y间的单向流量就是X的每个元素流给Y的每个元素的和。分配这些元素使得流量的最小值最大。

题解:流量的最小值最大意思就是尽可能平均分配的意思。一开始以为是

1 4 7
2 5 8
3 6 9

这样分配,但很显然是不对的,居然还给交了一发。

这种题可以考虑模仿样例的做法,给样例排序,变成:

1 6 7
2 5 8
3 4 9

是个蛇形填数?为什么蛇形填数是最平均的呢?我们考虑只有偶数列的情况(n为偶数的情况),每相邻两列内部的流量都是2,而与其他列的流量都相等。但奇数的话会始终多1列出来没有办法解决。而可以证明出已经达到了最优解(把所有的流量均分n份的最小值已经达到)。

所以写个蛇形填数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int g[305][305];

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int n;
    scanf("%d", &n);
    int cnt = 0, i = 1, j = 1, di = 1, dj = 0;
    while(cnt < n * n) {
        g[i][j] = ++cnt;
        i += di;
        j += dj;
        if(i == n && di == 1) {
            di = 0;
            dj = 1;
        } else if(i == n && dj == 1) {
            di = -1;
            dj = 0;
        } else if(i == 1 && di == -1) {
            di = 0;
            dj = 1;
        } else if(i == 1 && dj == 1) {
            di = 1;
            dj = 0;
        }
    }
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= n; ++j) {
            printf("%d%c", g[i][j], " \n"[j == n]);
        }
    }
}

D - Alice and the Doll

题意:一个n*m格子矩阵,放一个人偶在左上角向右走,只能在每个格子最多右转一次,有k个障碍物。求是否能够一次走完矩阵的所有非障碍物格然后停留在任意位置。

题解:在每个格子最多右转一次,相当于每个格子只能走一次,否则就出不来了。容易想到障碍物必须也是占据一些蛇形的片段,并且留下的位置刚好可以让人偶走一个蛇形的绕到中心,但是怎么判断障碍物的形状呢?百思不得其解遂看题解。题解表示观察到人偶撞墙或者撞障碍物必转向(自己走过的路也是墙)。然后暴力模拟一遍,每次在数据结构里面搜索前进方向上最近的墙/障碍物,走到它面前。至多进行n+m次。

所以选择一个结构就是set,维护行和列分别两堆set,一个障碍物同时插入行列两个set里面。然后墙/自己走过形成的墙就设置一个最值把多走的部分截断就可以了。判断答案的方法就是暴力统计走过的格子数是否等于空格数。

注:实现的时候卡在了样例4,因为会让人偶走回头路,但是也不能够简单让人偶移动0格就退出,比如一条竖线的矩阵。修复的方法是在人偶的后方一个格子塞一个障碍物。然后卡在样例9,看了dalao的代码才知道错在哪里,首先while循环的停止条件错了,不应该是d>u||r>l,应该是d>=u&&r>=l,因为是闭区间。这个改进再加上后面堵一个障碍物就可以防止原路返回了,人偶会在中间不断转圈圈。不过最简单的办法是记录人偶原地不动的次数,>=2次就break掉。

其实并不需要堵格子,记录原地不动的次数之后就不会原路返回了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
set<int> sr[100005], sc[100005];
 
int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int n, m, k;
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; ++i) {
        sr[i].insert(0);
        sr[i].insert(m + 1);
    }
    for(int i = 1; i <= m; ++i) {
        sc[i].insert(0);
        sc[i].insert(n + 1);
    }
    for(int i = 1; i <= k; ++i) {
        int r, c;
        scanf("%d%d", &r, &c);
        sr[r].insert(c);
        sc[c].insert(r);
    }
 
    int u = 1, d = n, l = 1, r = m;
    int x = 1, y = 1, dir = 1, stay = 0;
    ll cnt = 1;
    while(d >= u && r >= l) {
        if(dir == 1) {
            int ny = min(r, *sr[x].upper_bound(y) - 1);
            cnt += ny - y;
            if(ny == y) {
                if(stay == 0)
                    stay = 1;
                else
                    break;
            } else
                stay = 0;
            y = ny;
            ++u;
            r = y;
            dir = 2;
        } else if(dir == 2) {
            int nx = min(d, *sc[y].upper_bound(x) - 1);
            cnt += nx - x;
            if(nx == x) {
                if(stay == 0)
                    stay = 1;
                else
                    break;
            } else
                stay = 0;
            x = nx;
            --r;
            d = x;
            dir = 3;
        } else if(dir == 3) {
            int ny = max(l, *(--(sr[x].lower_bound(y))) + 1);
            cnt += y - ny;
            if(ny == y) {
                if(stay == 0)
                    stay = 1;
                else
                    break;
            } else
                stay = 0;
            y = ny;
            --d;
            l = y;
            dir = 4;
        } else {
            int nx = max(u, *(--(sc[y].lower_bound(x))) + 1);
            cnt += x - nx;
            if(nx == x) {
                if(stay == 0)
                    stay = 1;
                else
                    break;
            } else
                stay = 0;
            x = nx;
            ++l;
            u = x;
            dir = 1;
        }
    }
    puts((cnt == 1ll * n * m - k) ? "YES" : "NO");
}
posted @ 2019-11-13 20:51  KisekiPurin2019  阅读(121)  评论(0编辑  收藏  举报