首页 > 其他分享 >GYM 101522 La Salle-Pui Ching Programming Challenge 培正喇沙編程挑戰賽 2017

GYM 101522 La Salle-Pui Ching Programming Challenge 培正喇沙編程挑戰賽 2017

时间:2023-01-15 15:46:48浏览次数:93  
标签:Salle Pui La int res cin ret ans dp

C.Cheering

题意:

判断一个字符串中\(LSC\), \(PCMS\) 哪个字符穿出现的次数多,如果一样多输出\(Tie\)

思路:

模拟就行

    signed main() {
        std::string s;
        cin >> s;
        int res = 0, ret = 0;
        std::string A = "LSC", B = "PCMS";
     
        int N = s.size();
        for (int i = 0; i <= N - 3; i++) {
            if (s.substr(i, 3) == A) res++;
        }
     
        for (int i = 0; i <= N - 4; i++) {
            if (s.substr(i, 4) == B) ret++;
        }
        
        if (res == ret) puts("Tie");
        else puts(res > ret ? "LSC" : "PCMS");
        return 0;
    }

A.Ambiguous Dates

题意:

一共有 \(N\) 月,每一月有 \(D_i\) 天,要求对 \(D_\left[1\dots N\right]\) 重排序,使得最后日和月可以互换的合法天数最少。

思路:

因为月是从小到大递增的,应该尽可能的让第 \(i\) 月的 \(D_i\) 小,这样就可以防止 \(D_i\)月\(i\)日的日期合法。故将 \(D_i\) 从小到大排个序然后统计就好了。

    signed main() {
        int n; cin >> n;
        vector<int> d(n + 1);
        rep(i,1,n + 1) cin >> d[i];
     
        sort(d.begin(), d.end());
        long long ans = 0;
        rep(i,1, n + 1) if (d[i] > i) ans += min<long long>(n - i, d[i] - i);
     
        printf("%lld\n", ans * 2);
        return 0;
    }

B. Bacteria Experiment

题意:

给一个无向树,每一次可以让 \(u, v\) 相连边(当且仅当 \(u -> x -> v\) ), 求出多少次操作后可以让这棵树变成完全图。

思路:

首先假设这是一条链,现在模拟一下整个的流程



深度为 \(5\) 需要三次操作。由于每一次能够连的点的数量增长的十分快,所以应该去看深度而不是点数的变化,第一次深度为 \(1\) 的点可以连向深度为 \(3\) 的, 第二次深度为 \(1\) 的就可以连向深度为 \(4和5\) 的, 再下一次就可以连向深度为 \(6 7 8 9\) 的点,不难看出每一次都是在上一次的基础上 \(\times 2\) ,所以只需要知道树的直径的最高二进制位是多少。

    const int N = 500005;
     
    int h[N], idx;
    struct node {
        int v, nxt;
    }e[N * 2];
     
    void Add(int u, int v) {
        e[++idx] = {v, h[u]}, h[u] = idx;
    }
     
    int dep[N];
     
    void dfs(int u, int fa) {
        dep[u] = dep[fa] + 1;
        for (int i = h[u]; i; i = e[i].nxt) {
            int v = e[i].v;
            if (v == fa) continue;
            dfs(v, u);
        }
    }
     
    signed main() {
        int n; cin >> n;
        rep(i, 1, n) {
            int u, v;
            cin >> u >> v;
            Add(u, v);
            Add(v, u);
        }
     
        dfs(1, 0);
        int rt1 = 0, mx = 0;
        for (int i = 1; i <= n; i++) {
            if (dep[i] > mx) mx = dep[i], rt1 = i;
        }
     
        memset(dep, 0, sizeof(int) * (n + 1));
        dfs(rt1, 0);
        mx = *max_element(dep + 1, dep + 1 + n);
        mx--;
     
        int ans = 0, p = 1;
        while(p < mx) ans++, p *= 2;
        printf("%d\n", ans);
        return 0;
    }

D. Distribution of Days

题意:

求出 \(S \sim E\) 年间,\(M\) 月 \(D\) 日出现在一周七天内分别出现了多少次。

思路:

由于闰年是 \(4\) 年一闰 \(100\) 年不闰,\(400\) 年闰,所以可以将年分成 \(400\) 为一个循环,那么这 \(400\) 年中一共有 \(146097\) 天,而 \(7 \mid 146097\) 所以只需要用 \(400\) 作为一个循环节就可以了,预处理出来 \(2000\) 年到 \(2400\) 年 \(M.D\) 出现在周一到周日的次数就行了。

    int day[1000][13][40];
    int a[13];
     
    int check(int x) {
        if ((x % 400 == 0) || (x % 4 == 0 && x % 100 != 0)) return true;
        return false;
    }
     
    signed main() {
        rep(i, 1, 13) {
            if (i == 2) a[i] = 28;
            else if (i <= 7) a[i] = 30 + (i & 1);
            else if (i >= 8) a[i] = 30 + (i & 1 ^ 1);
        }
     
        int idx = 6;
        rep(i, 0, 400) {
            rep(j, 1, 13) {
                if (j == 2) {
                    for (int k = 1; k <= a[j] + check(i); k++) {
                        idx %= 7;
                        day[i][j][k] = ++idx, day[i + 400][j][k] = idx;
                    }
                } else {
                    for (int k = 1; k <= a[j]; k++) {
                        idx %= 7;
                        day[i][j][k] = ++idx, day[i + 400][j][k] = idx;
                    }
                }
            }
        }
     
        int q; cin >> q;
        while(q--) {
            int s, e, m, d;
            cin >> s >> e >> m >> d;
     
            array<long long, 8> ans{};
            if (e - s >= 400) {
                int p = (e - s) / 400;
                for (int i = s % 400; i <= s % 400 + 399; i++) ans[day[i][m][d]] += p;
            }
     
            e %= 400, s %= 400;
            if (e < s) e += 400;
            for (int i = s; i <= e; i++) ans[day[i][m][d]]++;
            rep(i, 1, 8) printf("%lld%c", ans[i], " \n"[i == 7]);
        }
     
        return 0;
    }

E. Expected Score

题意:

一共有 \(N\) 个数,\(2\times N\) 个箱子,每一个数出现在两个不同箱子上面。每一轮游戏取出两个箱子,如果这两个箱子上面的数字相同,就将这个数字该轮游戏的得分, 否则得分为\(0\),最后将箱子放回原位。现在一共有 \(R\) 轮游戏,已经进行了 \(R - 1\) 轮,求出最后一轮可以得到该轮游戏最高分的期望是多少。

思路:

由于我们可以知道前面 \(R - 1\) 轮取出的箱子他们表示的数是多少,所以我们可以把 \(N\) 个数分成三种情况来讨论,第一种是 \(i\) 在不同位置的箱子上出现了两次,第二种是 \(i\) 在已知的箱子中只有一个表示 \(i\), 第三种是 \(i\) 在已知的箱子中没有出现。将数分成了三类,那么将答案也分三种情况讨论,将 \(R - 1\) 轮最高分计为 \(res\), 第 \(R\) 轮的得分记为 \(ret\), 未知的箱子数量记为 \(M\)
第一种情况:在已知的箱子里面选出两个箱子作为最后的得分,很显然你一定是在 \(res + 1 \sim N\) 中选择两个数,否则答案一定不是最优的,那么这种情况的期望就是 \(ret\)
第二种情况:在已知的箱子中选择一个箱子在未知的箱子中选择一个,那么一定是 \(ret\) 在已知的箱子中只出现了一次,所以在 \(M\) 个箱子中只有一个箱子可能出现 \(ret\), 那么最后我们得到 \(ret\) 的概率是 \(\frac{1}{M}\), 而得到 \(res\) 的概率是 \(\frac{M - 1}{M}\) ,所以这种情况的期望是 \(ret\times \frac{1}{M} + res \times \frac{M - 1}{M}\)
第三种情况:在未知的箱子中选择两个数,选到 \(ret\) 的概率是 \(\frac{1}{M\times \left(M - 1\right)}\) ,选到 \(res\) 的概率是 \(1 - \sum\limits_{i = res + 1}^{N} i \times \frac{1}{M\times \left(M - 1\right)} (i \in unkown)\) 所以最后的期望也就求出了。
最终的答案就是三种情况的期望取最大值

    signed main() {
        int n, r;
        cin >> n >> r;
        
        std::vector<int> cnt(n + 1), used(n * 2 + 1);
        int cur = 0;
        int M = 0;
        rep(i, 1, r) {
            int x, y, cx, cy;
            cin >> x >> y >> cx >> cy;
            if (!used[x]) used[x] = 1, M++, cnt[cx]++;
            if (!used[y]) used[y] = 1, M++, cnt[cy]++;
            if (cx == cy) cur = max(cur, cx);
        }
     
        M = 2 * n - M;  // total of unopened boxes
        int res = 0;
        // case1 : both opened
        for (int i = 1; i <= n; i++) {
            if (cnt[i] == 2) res = i;
        }
     
        res = max(res, cur);
        double ret = 0;
        std::vector<int> open, unopen;
        rep(i, 1, n + 1) {
            if (cnt[i] == 1 && cur < i) open.push_back(i);
            else if (!cnt[i] && cur < i) unopen.push_back(i);
        }
     
        ret = max<double>(ret, res);
        // case2 : one open one unopen
        for (auto& x : open) ret = max<double>(ret, x * 1.0 / M + cur * (M - 1.0) / M);
     
        //case3 : both unopen
        double ans = 0, now = 0;
        for (auto& x : unopen) {
            ans += 2.0 / M / (M - 1) * x;
            now += 2.0 / M / (M - 1);
        }
     
        ans += (1.0 - now) * cur;
        
        ret = max<double>(ans, ret);
        printf("%.10lf\n", ret);
     
        return 0;
    }

F. Frustrating Game

题意:

给 \(N\) 个排成一行的仅由 \(R B\) 组成的球,可以使用魔法将 \(r\)个红球和 \(b\) 个蓝球消掉变成白球,但是这些球中间不能够有白球。

思路:

如果\(r = 1 \&\& b = 1\) 那么就可以将\(RB\) 串看作是一个括号序列,让我们求出该匹配方案,这就只需要用一个栈来作括号序列的匹配就可以了。那么类比到一般的情况,就每次判断在栈顶的 \(r + b\) 的元素中是不是有 \(r\) 个 \(R\) \(b\) 个 \(B\),是的话就删掉这些数,最后将方案倒着输出一遍就行。

    int stk[100005], top;
    std::string s; 
    int n, r, b;
    int sum[100005][2];
     
    signed main() {
        cin >> n >> r >> b;
        cin >> s;
     
        int R = 0, B = 0;
        for (auto& ch : s) R += (ch == 'R'), B += (ch == 'B');
        if (R % r || B % b || (R / r != B / b)) {
            puts("NO");
            return 0;
        }
     
        puts("YES");
        vector<vector<int>> ans;
        int res = 0, ret = 0;
        for (int i = 0; i < static_cast<int>(s.size()); i++) {
            res += s[i] == 'B';
            ret += s[i] == 'R';
            sum[i][0] = res;
            sum[i][1] = ret;
            stk[++top] = i;
            if (top > r + b) {
                int p = stk[top - r - b];
                if (res - sum[p][0] == b && ret - sum[p][1] == r) {
                    vector<int> cur;
                    for (int j = 1; j <= r + b; j++) {
                        res -= s[stk[top]] == 'B';
                        ret -= s[stk[top]] == 'R';
                        cur.push_back(stk[top]);
                        top--;
                    }
     
                    reverse(cur.begin(), cur.end());
                    ans.emplace_back(cur);
                }
            }
        }
        
        if (top) {
            assert(top == r + b);
            vector<int> cur;
            for (int i = 1; i <= top; i++) {
                cur.push_back(stk[i]);
            }
            ans.emplace_back(cur);
        }
     
        reverse(ans.begin(), ans.end());
        printf("%d\n", static_cast<int>(ans.size()));
        for (auto& vec : ans) {
            for (auto& x : vec) {
                printf("%d ", x + 1);
            }
            puts("");
        }
     
        return 0;
    }

G. Gravitational Tetris

题意:

有 \(8\) 种积木,且可以自由下落,构造一组方案使得最后, \(10\) 列拥有一样的行数。

思路:

可以将所有前 \(9\) 列的格子都拼到 \(40\) 行,最后一列只有可能是 \(cnt \% 4 = 2\ or\ 0\) 这样才是有解的,前面 \(9\) 列的拼法就是 \(h_i + 4x + 3y = 40\) 且 \(y \leq 3\), 采用 \(A, G\) 两种积木来拼,同时需要去更新 \(a_{i + 1}\), 因为 \(G\) 种积木他向右伸出了一格,最后一列特判一下就行了,如果是 \(cnt \% 4 = 2\) 的情况需要用一个 \(E\) 种积木和两个 \(B\) 来调整所有列变成 \(41\) 行。

    int h[11];
    int cnt[10];
     
    signed main() {
        rep(i, 0, 10) {
            cin >> h[i];
        }
     
        string s;
        vector<int> ret;
        int sum = 0;
        rep(i, 0, 9) {
            int d = 40 - h[i];
            int p = d / 4;
            int a = 0, g = 0;
            for (int j = p; j >= 0; j--) {
                if ((40 - h[i] - 4 * j) % 3 == 0) {
                    a = j, g = (40 - h[i] - 4 * j) / 3;
                    break;
                }
            }
            h[i + 1] += g;
            h[i] = 40;
            rep(j, 0, a) s += 'A', ret.push_back(i + 1);
            rep(j, 0, g) s += 'G', ret.push_back(i + 1);
            sum += a + g;
        }
     
        if (h[9] % 4 == 2) {
            for (int i = 0; i < (38 - h[9]) / 4; i++) {
                sum++;
                s += 'A';
                ret.push_back(10);
            }
            s += 'E', ret.push_back(9);
            s += 'B', ret.push_back(1), s += 'B', ret.push_back(5);
            sum += 3;
        } else if (h[9] % 4 == 0) {
            for (int i = 0; i < (40 - h[9]) / 4; i++) {
                s += 'A';
                ret.push_back(10);
                sum++;
            }
        }
     
        printf("%d\n", sum);
        for (int i = 0; i < s.size(); i++) {
            printf("%c %d\n", s[i], ret[i]);
        }
     
        return 0;
    }

H. Hit!

题意:

求出两圆任意一个公共点

思路:

计算几何板子,也可以用定比例分点、

    using namespace Geometry;

    int main() {
        std::cin.tie(nullptr)->sync_with_stdio(false);
        std::cout << std::fixed << std::setprecision(10);
        Circle p1, p2;
        cin >> p1.p >> p1.r;
        cin >> p2.p >> p2.r;

        if (intersect(p1, p2) > 1) {    //相交或外切
            auto ret = crosspoint(p1, p2);
            Point res = ret.first + ret.second;
            cout << res.real() / 2 << " " << res.imag() / 2 << "\n";
        } else {    //内切或者内含
            if (p1.r > p2.r) std::cout << p2.p << "\n";
            else std::cout << p1.p << "\n";
        }
    }

I. Inverted Signs

题意:

可以改变 \(\left[l \dots r\right]\) 区间内所有数变成他们的相反数,求出最后整个序列 \(\sum\limits_{i = 1}^{n - 1} \left| h_{i} - h_{i + 1}\right|\) 的最小值。

思路:

因为区间内改变正负性,不会影响这个区间对答案的贡献,所以只需要计算出相邻两项变化值是多少,最后取最小的两个负数加到原来的答案上就可以了

    signed main() {
        int n; cin >> n;
        long long ans = 0;
        vector<int> a(n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
     
        vector<long long> res(n);
        for (int i = 1; i < n; i++) {
            ans += abs(a[i] - a[i - 1]);
        }
     
        for (int i = 0; i < n - 1; i++ ) {
            res[i] = abs(-a[i + 1] - a[i]) - abs(a[i + 1] - a[i]);
        }
     
        sort(res.begin(), res.end());
        if (res[0] < 0) ans += res[0];
        if (res[1] < 0) ans += res[1];
     
        printf("%lld\n", ans);
     
        return 0;
    }

J. Juicy Candies

题意:

有 \(b\) 个 \(B\), \(r\) 个 \(R\), \(s\) 个 \(S\), 且相邻的不能相同,求字典序第 \(k\) 小的方案。

思路:

求出字典序第 \(k\) 小也就是要求出对应的一个前缀,计算这个前缀的方法可以只关心最后一个字符和前面一共有多少个 \(R B S\), 这样我们可以转化成求出求一个后缀,这样就只需要关心这个后缀中有多少个\(RBS\) 和第一个字符是什么。这样就可以用一个 \(dp\) 来转移方案数,定义 \(dp[i][b][s][r]\) 表示现在用了\(b\) 个 \(B\), \(r\) 个 \(R\), \(s\) 个 \(S\), 且在最开头是 \(i \in \{0, 1, 2\}\) 的方案有多少种,转移方程也很容易想到就是从另外两个状态转移过来, 但是如果直接计算方案数的话,可能会爆 \(long\ long\), 我们只需要关心前 \(k\) 个的方案,所以后面的就不需要去关心,所以最后 \(dp[i][b][r][s]\) 都要和 \(k + 1\) 取个最小值

    long long dp[210][210][210][3];
     
    signed main() {
        int b, r, s;
        long long k;
        cin >> b >> r >> s >> k;
        
        std::string ans;
        dp[1][0][0][0] = dp[0][1][0][1] = dp[0][0][1][2] = 1;
        rep(i, 0, b + 1) rep(j, 0, r + 1) rep(p, 0, s + 1) rep(c, 0, 3) {
            if (c == 0 && i) dp[i][j][p][c] += dp[i - 1][j][p][1] + dp[i - 1][j][p][2];
            if (c == 1 && j) dp[i][j][p][c] += dp[i][j - 1][p][2] + dp[i][j - 1][p][0];
            if (c == 2 && p) dp[i][j][p][c] += dp[i][j][p - 1][1] + dp[i][j][p - 1][0];
            dp[i][j][p][c] = min(dp[i][j][p][c], k + 1);
        }
     
        long long sum = 0;
        for (int i = 0; i < 3; i++) sum += dp[b][r][s][i];
        if (sum < k) { puts("None"); return 0; }
        
        int N = b + r + s;
        int pre = -1;
        for (int i = 1; i <= N; i++) {
            for (int j = 0; j < 3; j++) if (pre ^ j) {
                if (dp[b][r][s][j] >= k) {
                    if (b && j == 0) b--, ans += 'B';
                    if (r && j == 1) r--, ans += 'R';
                    if (s && j == 2) s--, ans += 'S';
                    pre = j;
                    break;
                } else {
                    k -= dp[b][r][s][j];
                }
            } 
        }
        
        puts(ans.c_str());
     
        return 0;
    }

K. Knights

题意:

\(N\times M\)矩形,同行或同列已被占领的格子中间的所有格子都会被占领,求在给定已占领格子的基础上占领所有格子还至少需要派骑士占领多少个格子

思路:

判断四个角就行,另外特判 \(1\times 1\), \(N\times 1\), \(1\times M\) 的情况

    signed main() {
        int n, m, k;
        cin >> n >> m >> k;
        int ans = 4;
        std::set<std::pair<int, int>> S;
        rep(i, 0, k) {
            int x, y;
            cin >> x >> y;
            S.insert(make_pair(x, y));
        }
     
        if (n == m && n == 1) {
            ans = 1;
            ans -= S.count(make_pair(1, 1));
        } else if (n == 1 || m == 1) {
            ans = 2;
            ans -= S.count(make_pair(1, 1));
            ans -= S.count(make_pair(n, m));
        } else {
            ans -= S.count(make_pair(1, 1));
            ans -= S.count(make_pair(n, 1));
            ans -= S.count(make_pair(1, m));
            ans -= S.count(make_pair(n, m));
        }
     
        printf("%d\n", max(0, ans));
        return 0;
    }

L. Let Me Count The Ways

题意:

给定两行,分别 \(n\) 列,\(m\) 列 \(\left(n \geq m\right)\), 将\(1\sim n + m\)个数放入这 $n + m $个格子,使得数字不重复、每行前面的数比后面的数大,每列第一行的数比第二行的数大,求有几种放法。对 \(10^9 + 7\) 取模

思路:

是一个标准杨表的问题,答案就是卡特兰数 \(\binom{n + m}{n} - \binom{n + m}{n + 1}\) 证明参考:L题解
但是这个数太大了 \(N, M \leq 10^9\) 肯定是不可以直接算出所有的 $1\sim 10^9 $ 阶乘和逆元的,所以考虑\(Lucas\) 定理,确实可以解决这个问题,但是在第五个样例的测试的时候要跑 \(22s\) 显然不能够通过本题,那么就要使用分段打表的技巧,预处理出来 \(1000000!, 2000000!, \dots, 10^9!\), 这样算任意一个数的阶乘的时候最多只需要算 \(1000000\) 次就可以算出来了,而且大于 \(10^9 + 7\) 的数的阶乘不需要去计算答案肯定是 \(0\)

    const int B = 1e6;
     
    Mint fac[B] = {...};  //预处理出来的阶乘
     
    Mint calc(long long x) {
        Mint res = fac[x / B];
        for (int i = x / B * B + 1; i <= x; i++) res *= i;
        return res;
    }
     
    signed main() {
        long long n, m; cin >> n >> m;
     
        if (n + m >= P) return printf("0\n"), 0;
        else {
            Mint a = calc(n + m);
            Mint b = calc(m - 1), c = calc(n + 1), d = calc(m), e = calc(n);
            
            printf("%d\n", (a / d / e - a / b / c)());
        }
     
        return 0;
    }

标签:Salle,Pui,La,int,res,cin,ret,ans,dp
From: https://www.cnblogs.com/Haven-/p/17053368.html

相关文章

  • openmmlab框架精简解读
    上面是整体的训练流程,像更新参数,打印日志,训练几轮去跑验证集等功能都是通过钩子实现的,每一个功能都实现一个钩子,一共有22个钩子的位点分别为:......
  • (已解决)Communications link failure The last packet sent successfully to the s
    使用mybatis逆向工程时,连接数据库出现如下错误:CommunicationslinkfailureThelastpacketsentsuccessfullytotheserverwas0millisecondsago.Thedriverhas......
  • upload-labs通关详解
    upload-labs通关详解一、Pass-01查看源码,发现变量前有var,可以代码语言是JSsubstring():裁剪字符串lastIndexOf():返回某个子字符串在字符串中最后出现的位置varext_name=......
  • 【Elastic Search】Logstatsh & FileBeat
    Logstash:Data转换,分析,提取,丰富及核心操作:https://blog.csdn.net/UbuntuTouch/article/details/100770828Beats家族(如FileBeat)参考:ES官方博客:Beats入门教程......
  • elasticsearch实现基于拼音搜索
    1、背景一般情况下,有些搜索需求是需要根据拼音和中文来搜索的,那么在elasticsearch中是如何来实现基于拼音来搜索的呢?可以通过elasticsearch-analysis-pinyin分析器来实现。......
  • elasticsearch实现基于拼音搜索
    目录1、背景2、安装拼音分词器3、拼音分词器提供的功能4、简单测试一下拼音分词器4.1dsl4.2运行结果5、es中分词器的组成6、自定义一个分词器实现拼音和中文的搜索1、创......
  • AtCoder Regular Contest 153
    喜提全场独一无二的score!ATC还是很友善的,如果每题等分就寄了A签到B真的是凭着实力不会做的呀。。。太菜了发现两维可以分别做,所以考虑一维的情况,然而并不会对于两......
  • ORB-SLAM2: an Open-Source SLAM system for Monocular,Stereo and RGB-D Camera
    摘要本文提出ORB-SLAM2一个完整的SLAM系统,用于单目,双目以及RGB-D相机,包括地图重用,回环检测以及重定位能力。本系统工作在实时的标准CPU,在更宽泛的环境中,来自于手持的室内......
  • Error:java: Compilation failed: internal java compiler error 解决办法
    编译时提示错误 Error:java:Compilationfailed:internaljavacompilererror 1、查看项目的jdk(Ctrl+Alt+shift+S)File->ProjectStructure->ProjectSettings->......
  • Laravel Valet - macOS 极简主义者的开发环境
    1.Lar**elValet介绍2.Lar**elValet安装3.测试Lar**elValet4.PHP版本5.服务站点6.定制Valet驱动7.Valet常用命令1.Lar**elValet介绍Lar**elValet 是m......