2023牛客寒假算法基础集训营1

2023牛客寒假算法基础集训营1

https://ac.nowcoder.com/acm/contest/46800
过了7题,写一半没撑住去睡觉了。

官方难度预期:
签到:ALCH
简单:DKM
中等:GFE
困难:BIJ

果然我还是很菜哇qaq
补了一下题发现确实GFE不难,其中F需要阅读理解细心一点,E计算几何需要debug的耐心,G需要有手玩几个样例找规律的耐心。
B学到前缀和优化dp。剩下两题一个是不太会实现,一个是不太懂题姐
总结: 沉下心来多想想,别急。

A - World Final? World Cup! (I)

模拟

#include <bits/stdc++.h>

using namespace std;

void solve() {
    string s;
    cin >> s;
    s = ' ' + s;
    int cnt1 = 0, cnt2 = 0;
    for (int i = 1; i <= 10; i++) {
        if (i & 1)    cnt1 += s[i] == '1';
        else    cnt2 += s[i] == '1';
        if (cnt1 == cnt2)    continue;
        int dx = abs(cnt1 - cnt2), add = (10 - i) / 2;
        if (i & 1) {
            if (cnt2 < cnt1)    add ++;
        }
        if (add < dx) {
            cout << i << endl;
            return;
        }
    }
    //cout << cnt1 << ' ' << cnt2 << endl;
    cout << -1 << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
}

B - World Final? World Cup! (II)

非常好题姐:https://www.cnblogs.com/fried-chicken/p/13736254.html
学到了前缀和优化dp的思想
还是稍微复杂一点就不会设状态啊悲。

#include <bits/stdc++.h>

using namespace std;
const int N = 45 * 3, M = 105, mod = 998244353;
int n, m, x, y, ans;
int f[N][M][M]; //f[i][j][s][t]: 比完i场, 1总分j, 1赢了s次, 2赢了t次
int up[N][M][M], mid[N][M][M], down[N][M][M]; //上三角, 对角线, 下三角的dp前缀和

int main () {
    cin >> n >> m >> x >> y;
    f[0][0][0] = 1;
    for (int i = 1; i <= n; i++) { //滚掉一维
        for (int j = 0; j <= 3 * n; j++) { //预处理前缀和
            //init
            for (int u = 0; u <= x; u++)    down[j][u][0] = mid[j][u][0] = f[j][u][0];
            for (int v = 0; v <= y; v++)    up[j][0][v] = mid[j][0][v] = f[j][0][v];
            //prefix-mid
            for (int u = 1; u <= x; u++) {
                for (int v = 1; v <= y; v++) {
                    mid[j][u][v] = (mid[j][u-1][v-1] + f[j][u][v]) % mod;
                }
            }
            //prefix-up/down
            for (int u = 0; u <= x; u++) {
                for (int v = 0; v <= y; v++) {
                    if (u)  up[j][u][v] = (up[j][u-1][v] + mid[j][u][v]) % mod;
                    if (v)  down[j][u][v] = (down[j][u][v-1] + mid[j][u][v]) % mod;
                }
            }
        }

        for (int j = 0; j <= 3 * n; j++) {
            for (int u = 0; u <= x; u++) {
                for (int v = 0; v <= y; v++) {
                    f[j][u][v] = 0; //为啥这里一定要初始化
                    if (v)      (f[j][u][v] += down[j][u][v-1]) %= mod;
                    if (j >= 1)     (f[j][u][v] += mid[j-1][u][v]) %= mod;
                    if (j >= 3 && u)     (f[j][u][v] += up[j-3][u-1][v]) %= mod;
                }
            }
        }
    }

    for (int j = m; j <= 3 * n; j++)    (ans += f[j][x][y]) %= mod;
    cout << ans << endl;
}

C - 现在是,学术时间 (I)

贪心。全部放到一个人身上

#include <bits/stdc++.h>

using namespace std;

void solve() {
    int n, cnt = 0;
    cin >> n;
    while (n --) {
        int x;
        cin >> x;
        if (x)  cnt++;
    }
    cout << cnt << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
}

D - 现在是,学术时间 (II)

贪心构造,画图模拟点在内部和在外部的情况进行模拟。

#include <bits/stdc++.h>

using namespace std;
double ans;

int main() {
    int t;
    cin >> t;
    while (t --) {
        int x, y, a, b;
        cin >> x >> y >> a >> b;
        if (a >= x && b >= y)    ans = 1.0 * x * y / (1.0 * a * b);
        else if (a >= x) {
            double j1 = 1.0 * x * b, j2 = 1.0 * x * (y - b), s1 = 1.0 * x * y + 1.0 * b * (a - x), s2 = 1.0 * x * y + 1.0 * (y - b) * (a - x);
            ans = max (j1 / s1, j2 / s2);
        }
        else if (b >= y) {
            double j1 = 1.0 * a * y, j2 = 1.0 * (x - a) * y, s1 = 1.0 * x * y + 1.0 * a * (b - y), s2 = 1.0 * x * y + 1.0 * (x - a) * (b - y);
            //cout << j1 << ' ' << j2 << ' ' << sum << endl;
            ans = max (j1 / s1, j2 / s2);
        }
        else {
            double s1 = 1.0 * a * b, s2 = 1.0 * (x - a) * b, s3 = 1.0 * a * (y - b), s4 = 1.0 * (x - a) * (y - b);
            ans = max ({s1, s2, s3, s4}) / (1.0 * x * y);
        }
        cout << fixed << setprecision (6) << ans << endl;
    }
}

E - 鸡算几何

偷jls的板子
原理:按照长度匹配,比较叉积

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

using T = double;
struct Point {
    T x;
    T y;
    Point(T x = 0, T y = 0) : x(x), y(y) {}
    
    Point &operator+=(const Point &p) {
        x += p.x, y += p.y;
        return *this;
    }
    Point &operator-=(const Point &p) {
        x -= p.x, y -= p.y;
        return *this;
    }
    Point &operator*=(const T &v) {
        x *= v, y *= v;
        return *this;
    }
    friend Point operator-(const Point &p) {
        return Point(-p.x, -p.y);
    }
    friend Point operator+(Point lhs, const Point &rhs) {
        return lhs += rhs;
    }
    friend Point operator-(Point lhs, const Point &rhs) {
        return lhs -= rhs;
    }
    friend Point operator*(Point lhs, const T &rhs) {
        return lhs *= rhs;
    }
};

T dot(const Point &a, const Point &b) {
    return a.x * b.x + a.y * b.y;
}

T cross(const Point &a, const Point &b) {
    return a.x * b.y - a.y * b.x;
}
void solve() {
    Point a[6];
    for (int i = 0; i < 6; i++)    cin >> a[i].x >> a[i].y;
    if (cross(a[0] - a[1], a[2] - a[1]) < 0) swap(a[0], a[2]);
    if (cross(a[3] - a[4], a[5] - a[4]) < 0) swap(a[3], a[5]);
    
    double len0 = sqrt(dot(a[0] - a[1], a[0] - a[1]));
    double len1 = sqrt(dot(a[3] - a[4], a[3] - a[4]));
    if (abs(len0 - len1) > 1E-9) cout << "YES\n";
    else cout << "NO\n";
}

int main() {
    int t;
    cin >> t;
    while (t --)    solve();
}

F - 鸡玩炸蛋人

难度在于读题。

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

using namespace std;
const int N = 1e5 + 5, M = 4e5 + 5;
int fa[N], sz[N], n, m, a[N];

int find (int x) {
    if (x != fa[x])     fa[x] = find (fa[x]);
    return fa[x];
}

void Union (int x, int y) {
    x = find (x), y = find (y);
    fa[x] = y, sz[y] += sz[x];
}

int main () {
    cin >> n >> m;
    for (int i = 1; i <= n; i++)    fa[i] = i, sz[i] = 1;
    while (m --) {
        int a, b;
        cin >> a >> b;
        //cout << find (a) << ' ' << find (b) << endl;
        if (find(a) != find (b))    Union (a, b);
    }
    for (int i = 1; i <= n; i++)    cin >> a[i];
    for (int i = 1; i <= n; i++) {
        //cout << find(i) << ' ';
        if (a[i])   a[find(i)] = 1;
    }
    //cout << endl;
    //for (int i = 1; i <= n; i++)    cout << a[i] << ' ';    cout << endl;
    int cnt = 0, pos = 0;
    for (int i = 1; i <= n; i++) {
        if (fa[i] == i && a[i])     cnt ++, pos = i; 
    }
    if (!cnt) { //无炸弹, 全部可达
        ll ans = 0;
        for (int i = 1; i <= n; i++) {
            if (fa[i] == i) ans += 1ll * sz[i] * sz[i];
        }
        cout << ans;
    }
    else if (cnt == 1)      cout << 1ll * sz[pos] * sz[pos];
    else        cout << 0 << endl; //两个终点, 矛盾了
}

G - 鸡格线

非常困难思维题,主要是想不到性质。

且总操作次数不大,可取20。

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

using namespace std;
const int N = 1e5 + 5;
int n, m, ans, a[N];

int f(int x) {
    return round(sqrt(x) * 10);
}

signed main() {
    set<int> s; //未收敛的值的下标
    scanf ("%lld%lld", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        ans += a[i];
        if (f(a[i]) != a[i])    s.insert(i);
    }

    s.insert(n + 1); //边界
    int op, l, r, k;
    while (m --) {
        scanf("%lld", &op);
        if (op == 1) {
            scanf("%lld%lld%lld", &l, &r, &k);
            int pos = l;
            while (1) {
                int cur = (*s.lower_bound(pos));
                if (cur > r)    break;
                int cnt = min (k, 20ll); //经验值, 最多20次
                while (cnt --) {
                    ans -= a[cur];
                    a[cur] = f(a[cur]);
                    ans += a[cur];
                }
                if (f(a[cur]) == a[cur])    s.erase(cur);
                pos = cur + 1;
            }
        }
        else    printf("%lld\n", ans);
    }
}

H - 本题主要考察了DFS

贪心。

#include <bits/stdc++.h>

using namespace std;

void solve() {
    int n;
    cin >> n;
    int a[4] = {0};
    for (int i = 1; i < n * n; i++) {
        string s;
        cin >> s;
        for (int j = 0; j < 4; j++) { //0,2  1,3
            if (s[j] == '0')    continue;
            if (s[j] == '1')    a[j] ++;
            else    a[(j + 2) % 4] --;
        }
    }
    //for (int i = 0; i < 4; i++)     cout << a[i] << ' ';cout << endl;
    int ans = 0;
    for (int i = 0; i < 4; i++)     ans += a[i];
    cout << 10 + ans << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
}

I - 本题也主要考察了DFS

J - 本题竟也主要考察了DFS

K - 本题主要考察了dp

贪心绑定 \(001\),查看剩余即可

#include <bits/stdc++.h>

using namespace std;
int n, m;

void solve() {
    cin >> n >> m;
    if (n - m < 2) {
        cout << max(0, n - 2) << endl;
        return ;
    }
    int cnt = (n - m) / 2 + 1;
    cout << max(0, m - cnt) << endl;
}

int main() {
    solve();
}

L - 本题主要考察了运气

如题。

#include<bits/stdc++.h>

using namespace std;

int main () {
    cout << 32;
}

M - 本题主要考察了找规律

线性dp。f[i][j]: 放完前i个人剩下j个

#include <bits/stdc++.h>

using namespace std;
const int N = 505;
double f[N][N], ans;

int main() {
    int n, m;
    cin >> n >> m;

    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) { //还剩j个
            for (int k = 0; k <= j; k++) { //这个人拿了k个
                if (j)    f[i][j] = max (f[i][j], f[i-1][j-k] + 1.0 * k / j);
            }
            ans = max (ans, f[i][j]);
        }
    }
    cout << fixed << setprecision (7) << ans;
}

//f[i][j]: 放完前i个人剩下j个
posted @ 2023-01-16 22:30  Sakana~  阅读(98)  评论(0编辑  收藏  举报