• 牛客挑战赛38 (A


    A - 多边形与圆

    题目链接

    题意

    给出一个多边形的坐标和圆的半径, 多边形可以在圆内滚动, 问点 1 在成为转动中心到下一次成为转动中心的过程中经过的路程长度.

    题解

    枚举点 2 - n 成为转动中心的情况下点 1 的路程.

    view code
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define inc(i, l, r) for (int i = l; i <= r; i++)
    
    const int maxn = 1e2 + 5;
    
    int n, r;
    double x[maxn], y[maxn], res;
    
    double dis(int i, int j) {
        return sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
    }
    // 这里应该用 atan2() 的
    double aatan(int i, int j) {
        if (x[i] == x[j]) {
            if (y[i] > y[j]) return M_PI / 2;
            return 3 * M_PI / 2;
        }
        return atan((y[i] - y[j]) / (x[i] - x[j]));
    }
    double alp(int i) {
        double r = aatan(i, i - 1) - aatan(i, i + 1);
        while (r < 0) r += M_PI;
        while (r > M_PI) r -= M_PI;
        return r;
    }
    
    int main() {
        cin >> n >> r;
        inc(i, 1, n) cin >> x[i] >> y[i];
        x[n + 1] = x[1], y[n + 1] = y[1];
        inc(i, 1, n - 1) {
            double d = dis(i, i + 1), d2 = dis(i + 1, i + 2), p = dis(1, i + 1);
            double a = asin(d / 2 / r), a2 = asin(d2 / 2 / r);
            res += (M_PI - a - a2 - alp(i + 1)) * p;
        }
        printf("%.12f
    ", res);
    }
    

    B - 子串翻转

    题目链接

    题意

    给出一个长度为 n 的字符串. 有 q 组操作, 一种是选择长度为 m 的区间 ([x_i, x_i + m - 1]) 翻转, 一种是查询串上某一位置的字符. 每次翻转操作的起始坐标 (x_i) 是严格不减的. n ≤ 1e6, q ≤ 1e6.

    题解

    根据操作坐标的单调性, 使用滑动窗口维护被翻转的区间. 滑动窗口是一段长度为 m 的区间, 用 std::deque 保存内部元素, 它会从左往右扫描一遍区间, 遇到翻转操作时, 打上翻转标记(或取消翻转标记)而不实际改变内部元素. 处于翻转状态时, 移动滑动窗口, 本来要弹出队首元素, 但实际上该元素在队尾, 所以执行pop_back(); 本来要往队尾加入元素, 但该区间是翻转状态, 所以应加入队首以正确应对查询. 时间复杂度 O(n).

    view code
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define inc(i, l, r) for (int i = l; i <= r; i++)
    
    const int maxn = 1e6 + 5;
    
    int n, m, q, x, y, f, top;
    char a[maxn];
    deque<char> deq;
    
    int main() {
        scanf("%d %d %d", &n, &m, &q);
        scanf("%s", a + 1);
        inc(i, 1, m) deq.push_back(a[i]);
        top = 1;
        while (q--) {
            scanf("%d %d", &x, &y);
            if (x == 1) {
                while (top < y) {
                    if (f) {
                        a[top] = deq.back();
                        deq.pop_back();
                        deq.push_front(a[top + m]);
                    } else {
                        a[top] = deq.front();
                        deq.pop_front();
                        deq.push_back(a[top + m]);
                    }
                    top++;
                }
                f = 1 - f;
            } else {
                if (y >= top && y < top + m) {
                    if (f)
                        printf("%c", deq[m - 1 - (y - top)]);
                    else
                        printf("%c", deq[y - top]);
                } else
                    printf("%c", a[y]);
            }
        }
        printf("
    ");
    }
    

    C - 圆桌聚餐

    题目链接

    题意

    A 国人与 B 国人在一张有 n 个位置的圆桌上吃饭, A 国人不会坐在旁边有人的位置, B 国人不会坐在距离 2 以内有人的位置. 刚开始圆桌上没有人, 接下来会有 p / (p + q) 的概率走进一个 A 国人, q / (p + q) 的概率走进一个 B 国人; 他会随机选择一个位置, 如果不符合他的要求就直接离开. 问最后不能再容纳人时人数的期望, 对 998244353 取模. n ≤ 3e5, 0 ≤ p, q < 998244353.

    题解

    首先第一个人坐下时不受任何限制, 且每个人坐下后是无差别的. 所以问题变成考虑剩下的 n - 1 个连续的位置能容纳多少人. 定义 dp[i] 为连续 i 个位置能容纳人的期望, p 不为 0 时有 dp[1] = dp[2] = 0, dp[3] = dp[4] = 1(p 为 0 时dp[3] = dp[4] = 0), 状态转移时枚举分隔位置即可. 推导出

    [dp[j] = frac{p × sum_{i=2}^{j-1}{(dp[i - 1] + dp[j - i])} + q × sum_{i=3}^{j-2}{(dp[i - 1] + dp[j - i])}} { p × (j - 2) + q × (j - 4) } + 1 = frac{2p × sdp[j - 2] + 2q × sdp[j - 3]} {p × (j - 2) + q × (j - 4)} + 1 ]

    其中 sdp[] 是 dp[] 的前缀和

    view code
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define inc(i, l, r) for (int i = l; i <= r; i++)
    
    const int maxn = 3e5 + 5;
    const int mod = 998244353;
    
    ll dp[maxn], s[maxn];
    int n;
    ll p, q;
    
    inline ll ksm(ll a, ll n) {
        ll r = 1;
        while (n) {
            if (n & 1) r = r * a % mod;
            a = a * a % mod;
            n >>= 1;
        }
        return r;
    }
    
    int main() {
        scanf("%d %lld %lld", &n, &p, &q);
        if (p) dp[3] = dp[4] = s[3] = 1, s[4] = 2;
        inc(i, 5, n - 1) {
            dp[i] = ((2 * p * s[i - 2] + 2 * q * s[i - 3]) % mod *
                         ksm((p * (i - 2) + q * (i - 4)) % mod, mod - 2LL) +
                     1) %
                    mod;
            s[i] = (s[i - 1] + dp[i]) % mod;
        }
        printf("%lld
    ", (dp[n - 1] + 1) % mod);
    }
    

    D - 突击检查

    题目链接

    题意

    有一个具有 n 个点的树, 给出每个点的度数. 已知有 X 个点被打上了标记, 记被打标记的点之间形成的联通块数为 x, 求 (E(x^2)). n ≤ 2e6.

    题解

    只考虑被打标记的点及其之间的边, 得到 (x = 点数 - 边数). 则 (x^2 = 点数^2 - 2 × 点数 × 边数 + 边数^2), 边数的期望由每一条边的贡献得到, 即

    [frac{(n - 1) × C_{n-2}^{X-2}}{C_{n}^{X}} ]

    而边数的平方是枚举所有的边对, 这个时候要分类讨论选出的两条边的共同点个数是 0, 1 还是 2, 题解定义了一个 (adj = sum_{i=1}^{n}du[i] × (du[i] - 1)), 它的意义是对于每个点选出两条不同的边对数. 最后得到是

    [frac { [(n - 1) × (n - 2) - adj] × C_{n-4}^{X-4} + adj × C_{n-3}^{X-3} + (n - 1) × C_{n-2}^{X-2} }{C_n^X} ]

    这题调了半天发现 2000000 少打了一个 0.

    view code
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define inc(i, l, r) for (int i = l; i <= r; i++)
    
    const int maxn = 2e6 + 5;
    const int mod = 998244353;
    
    ll du[maxn], adj, res;
    ll n, x;
    
    inline ll ksm(ll _a, ll _n) {
        ll _r = 1;
        while (_n) {
            if (_n & 1) _r = _r * _a % mod;
            _a = _a * _a % mod;
            _n >>= 1;
        }
        return _r;
    }
    ll fac[maxn];
    inline void getfac() {
        fac[0] = 1;
        inc(i, 1, 2000000) fac[i] = fac[i - 1] * i % mod;
    }
    inline ll C(ll _n, ll _m) {
        if (_n < 0 || _m < 0 || _n < _m) return 0;
        return fac[_n] * ksm(fac[_n - _m], mod - 2LL) % mod *
               ksm(fac[_m], mod - 2LL) % mod;
    }
    inline ll inv(ll _n) { return ksm(_n, mod - 2LL); }
    
    int main() {
        getfac();
        scanf("%lld %lld", &n, &x);
        inc(i, 1, n) {
            scanf("%lld", &du[i]);
            adj = (adj + du[i] * (du[i] - 1)) % mod;
        }
        res = (x * x % mod -
               (2 * x - 1) * (n - 1) % mod * C(n - 2, x - 2) % mod * inv(C(n, x)) %
                   mod +
               ((n - 1) * (n - 2) % mod + mod - adj) % mod * C(n - 4, x - 4) % mod *
                   inv(C(n, x)) % mod +
               adj * C(n - 3, x - 3) % mod * inv(C(n, x)) % mod + mod) %
              mod;
        printf("%lld
    ", res);
    }
    
  • 相关阅读:
    File操作
    集合
    几个python资料地址
    Case1-用list写shoppingcart
    字符串操作
    运算-Dictionary
    运算-list
    Python数据类型
    标准库和库导入
    Pycharm
  • 原文地址:https://www.cnblogs.com/linqi05/p/12537451.html
Copyright © 2020-2023  润新知