蔚来杯2022牛客暑期多校训练营1

比赛链接

A

题解

知识点:贪心。

将区间按左端点排序,合并区间,记录所有区间之间断开的长度即可。

时间复杂度 \(O(n\log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

struct node {
    ll l, r;
}a[200007];


int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 0;i < n;i++) {
        int x, r;
        cin >> x >> r;
        a[i].l = x - r;
        a[i].r = x + r;
    }
    sort(a, a + n, [&](node a, node b) {return a.l < b.l;});
    ll ans = 0, prer = a[0].r;
    for (int i = 1;i < n;i++) {
        if (a[i].l > prer)ans += a[i].l - prer;
        prer = max(prer, a[i].r);
    }
    cout << ans << '\n';
    return 0;
}

C

题解

知识点:枚举,计算几何。

首先注意到一行中靠右的影响包含于靠左的,因此每行只需要考虑最左侧的位置 \(Xrow\)。然后枚举下到上所有行的最左侧点 \(Xrow\) 与端点 \((0,1)\) 连成射线算出斜率 \(k\) ,将射线右侧(包括射线上)的点都排除,记录最右可行点的横坐标 \(Xgd0\),端点 \((0,m)\) 类似。

每次询问更新起作用的点,分别枚举上下端点的射线,将两次枚举每行可行横坐标中最小的加起来就是答案,要注意可能会超过 \(n\) ,因此需要 min({n,Xgd0[y],Xgd1[y]})

注意 \(k = 0\) 时特判。

时间复杂度 \(O(q(m+k))\)

空间复杂度 \(O(k+m)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

int X[200007], Y[200007], Xrow[200007], Xgd0[200007], Xgdm[200007];

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m, k, q;
    cin >> n >> m >> k >> q;
    for (int i = 1;i <= k;i++) cin >> X[i] >> Y[i];
    while (q--) {
        int id;
        cin >> id;
        cin >> X[id] >> Y[id];
        for (int y = 1;y <= m;y++) Xrow[y] = n + 1;
        for (int i = 1;i <= k;i++) Xrow[Y[i]] = min(Xrow[Y[i]], X[i]);///更新某行最靠前的人
        double k = 0;
        for (int y = 1;y <= m;y++) {
            k = max(k, 1.0 * (y - 1) / Xrow[y]);///更新(0,1)射线最大斜率
            if (k == 0) Xgd0[y] = Xrow[y] - 1;///斜率为0
            else Xgd0[y] = 1.0 * (y - 1) / k - 1e-9;///(x,x+1] = x,最大斜率与第y行交点(可能超过n)
        }
        k = 0;
        for (int y = m;y >= 1;y--) {
            k = min(k, 1.0 * (y - m) / Xrow[y]);///更新(0,m)射线最小斜率
            if (k == 0) Xgdm[y] = Xrow[y] - 1;
            else Xgdm[y] = 1.0 * (y - m) / k - 1e-9;
        }
        ll ans = 0;
        for (int y = 1;y <= m;y++) ans += min({ n,Xgd0[y],Xgdm[y] });
        cout << ans << '\n';
    }
    return 0;
}

D

题解

知识点:思维,计算几何。

可以证明线段平行于线段中点与圆心的连线时,覆盖弧长最大。

但要注意精度问题,不能操作太多次,尽量将计算过程化简。

时间复杂度 \(O(t)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

bool solve() {
    double r, x, y, d;
    cin >> r >> x >> y >> d;
    double len = sqrt(x * x + y * y);
    double radA = acos((len - d) / r);
    double radB = acos((len + d) / r);
    cout << fixed << setprecision(7) << r * (radA - radB) << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

G

题解

知识点:思维。

签到题。可以肯定的是,答案一定是 string(s.length() - 1, '9') 或者其本身。因为前者一定存在,若要大于前者,则长度大于前者,即长度是原串长度,而符合原串长度且数字意义上不超过原串的最大字符串一定是其本身。因此只需要构造一个全是 \(9\) 长度为原串长度减一的字符串,输出其和原串较大的一个即可。

时间复杂度 \(O(|S|)\)

空间复杂度 \(O(|S|)\)

代码

#include <bits/stdc++.h>

using namespace std;

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    string s1;
    cin >> s1;
    string s2(s1.length() - 1, '9');
    cout << (s1 > s2 ? s1 : s2) << '\n';
    return 0;
}

I

题解

知识点:概率dp。

题面长的离谱。

给出 \(13\) 张牌,相同类型的牌不超过 \(2\) 个,牌堆 \(123\) 张牌,问形成7对子的期望局数。

显然需要逆推,因为直到剩余 \(0\) 张牌时期望局数一定为 \(0\) 。因此 \(dp[i][j]\) 表示剩余 \(i\) 张牌还剩 \(j\) 张单牌的期望局数,转移方程为:

\[dp[i][j] = \frac{3j}{i}(dp[i-1][j-2]+1) + \frac{i-3j}{i}(dp[i-1][j] + 1) \]

注意 \(i\geq3j\) 时才可能发生,\(j \leq 2\) 时期望特判为 \(0\)

时间复杂度 \(O(t)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int mod = 1e9 + 7;
int dp[14], inv[124];

void inverse(int n) {
    inv[1] = 1;
    for (int i = 2;i <= n;i++)
        inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod;
}

bool solve() {
    string s;
    cin >> s;
    set<int> st;
    int cnt = 13;
    for (int i = 0;i < s.size();i += 2) {
        if (!st.count((s[i] - '0') * 100 + (s[i + 1] - 'a')))
            st.insert((s[i] - '0') * 100 + (s[i + 1] - 'a'));
        else
            cnt -= 2;
    }
    cout << dp[cnt] << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    inverse(123);
    for (int i = 1;i <= 123;i++) {///还剩下多少张,不能摸了多少张,因为摸了第0张是要求的,而还剩0张单牌1~13的局数是0
        for (int j = 13;j >= 1;j--) {
            if (i >= 3 * j)
                dp[j] = ((1LL * 3 * j * dp[max(0, j - 2)] +
                    1LL * (i - 3 * j) * dp[j]) % mod * inv[i] + 1) % mod;///概率x期望 = 新期望,j=1时,没dp[j-2],但期望是0,因此要特判
        }
    }
    int t = 1;
    cin >> t;
    for (int i = 1;i <= t;i++) {
        cout << "Case #" << i << ": ";
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}
张贴在2