2024牛客暑期多校训练营2

A.Floor Tiles

恶心构造,本来构造的方法没有错,因为不小心修改了第一块砖的位置,导致一直过不去,没注意,倒了
思路:先把最多曲线的构造出来,就是类似于 B A B A B A BABABA BABABA或者 A B A B A B ABABAB ABABAB的去构造,具体是哪一种形式取决于第一块瓷砖是 A A A还是 B B B,如果第一块为 A A A并且 x x x y y y奇偶性一样就是 A B A B A B ABABAB ABABAB这种, 否则 B A B A B A BABABA BABABA,如果第一块为 B B B并且 x x x y y y奇偶性一样就是 B A B A B A BABABA BABABA这种, 否则 A B A B A B ABABAB ABABAB,如图在这里插入图片描述
在这里插入图片描述

红色部分就是旋转九十度以后会减少曲线的数量,每旋转一个就减少一条,所以我们构造出来以后,直接去计算红色部分的数量加上 n + m n + m n+m就是最多的曲线数,最少是 n + m n + m n+m,先把无解的情况判断了,在把需要减少的曲线数量算出来,记得不要修改题目所给地砖

void solve() {
    int n, m, k; cin >> n >> m >> k;
    int x, y; char t; cin >> x >> y >> t;
    vector g(n + 1, vector<char>(m + 1));
    if (t == 'A') {
        if ((x & 1) == (y & 1)) g[1][1] = 'A';
        else g[1][1] = 'B';
    } else {
        if ((x & 1) == (y & 1)) g[1][1] = 'B';
        else g[1][1] = 'A';
    }
    for (int i = 2; i <= m; i ++) {
        if (g[1][i - 1] == 'A') g[1][i] = 'B';
        else g[1][i] = 'A';
    }
    for (int i = 2; i <= n; i ++) {
        for (int j = 1; j <= m; j ++) {
            if (g[i - 1][j] == 'A') g[i][j] = 'B';
            else g[i][j] = 'A';
        }
    }
    int smx = n + m, smn = n + m;
    for (int i = 2; i <= n; i ++) {
        for (int j = 1; j < m; j ++) {
            if (g[i][j] == 'B') {
                smx ++;
            }
        }
    }
    if (k < smn || k > smx) {
        cout << "No\n";
        return ;
    }
    int cnt = smx - k;
    if (t == 'A') {
        for (int i = 2; i <= n && cnt > 0; i ++) {
            for (int j = 1; j < m && cnt > 0; j ++) {
                if (g[i][j] == 'B') {
                    g[i][j] = 'A';
                    cnt --;
                }
            }
        }
    } else {
        for (int i = 2; i <= n && cnt > 0; i ++) {
            for (int j = 2; j <= m && cnt > 0; j ++) {
                if (g[i][j] == 'A') {
                    g[i][j] = 'B';
                    cnt --;
                }
            }
        }
    }
    cout << "Yes\n";
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= m; j ++) {
            cout << g[i][j];
        }
        cout << '\n';
    }
}

B.MST

思路:根号分治,小于等于 n \sqrt{n} n 的直接 O ( n 2 ) O(n^2) O(n2)把需要的边选出来做克鲁斯卡尔,否则直接对所有的边做克鲁斯卡尔

struct S {
    int u, v, w;

};
vector<S> val;
map<array<int, 2>, int> g;
int n, m, q;
int fa[N];

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

void merge(int a, int b) {
    int pa = find(a), pb = find(b);
    fa[pa] = pb;
}

void solve() {
    cin >> n >> m >> q;
    val.resize(m + 1);
    for (int i = 1; i <= m; i ++) {
        int a, b, c; cin >> a >> b >> c;
        val[i] = {a, b, c};
        g[ {a, b}] = c;
        g[ {b, a}] = c;
    }
    sort(val.begin() + 1, val.end(), [&](S a, S b) {
        return a.w < b.w;
    });
    while (q --) {
        int k; cin >> k;
        vector<int> node(k + 1), query(n + 1);
        for (int i = 1; i <= k; i ++) {
            cin >> node[i];
            fa[node[i]] = node[i];
            query[node[i]] = 1;
        }
        vector<S> edge;
        int cnt = 0, sum = 0;
        if (k < 1000) {
            for (int i = 1; i <= k; i ++) {
                for (int j = i + 1; j <= k; j ++) {
                    if (i == j) continue;
                    int u = node[i], v = node[j];
                    if (g.count({u, v})) {
                        edge.push_back({u, v, g[{u, v}]});
                    }
                }
            }
            sort(edge.begin(), edge.end(), [&](S a, S b) {
                return a.w < b.w;
            });
            for (int i = 0; i < edge.size(); i ++) {
                auto [u, v, w] = edge[i];
                int pu = find(u), pv = find(v);
                if (pu != pv) {
                    cnt ++;
                    sum += w;
                    merge(u, v);
                }
            }
            if (cnt < k - 1) cout << -1 << '\n';
            else cout << sum << '\n';
        } else {
            for (int i = 1; i <= m; i ++) {
                auto [u, v, w] = val[i];
                int pu = find(u), pv = find(v);
                if (pu != pv && query[u] && query[v]) {
                    cnt ++;
                    sum += w;
                    merge(u, v);
                }
            }
            if (cnt < k - 1) cout << -1 << '\n';
            else cout << sum << '\n';
        }
    }
}

C.Red Walking on Grid

思路:直接 d p dp dp d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1]在第 i i i走第一行和第二行的最大步数,优先让左边的先转移,在转移上下的,去 m a x max max即可

void solve() {
    int n; cin >> n;
    string s1, s2; cin >> s1 >> s2;
    vector<array<int, 2>> dp(n + 1);
    int ans = 0;
    if (s1[0] == s2[0] && s1[0] == 'R') {
        dp[0][0] = dp[0][1] = 1, ans = 1;
    }
    for (int i = 1; i < s1.size(); i++) {
        if (s1[i] == 'R' && s1[i - 1] == 'R') {
            dp[i][0] = max(dp[i - 1][0] + 1, dp[i][0]);
        }
        if (s2[i] == 'R' && s2[i - 1] == 'R') {
            dp[i][1] = max(dp[i][1], dp[i - 1][1] + 1);
        }
        if (s1[i] == s2[i] && s1[i] == 'R') {
            int t0 = dp[i][0], t1 = dp[i][1];
            dp[i][0] = max(dp[i][0], t1 + 1);
            dp[i][1] = max(dp[i][1], t0 + 1);
        }
        ans = max(ans, max(dp[i][1], dp[i][0]));
    }
    cout << ans << '\n';
}

E.GCD VS XOR

思路:取二进制下从低位开始的第一个 1 1 1的位置和 n n n g c d gcd gcd即可,二的整次幂没有答案

void solve() {
    int n; cin >> n;
    for (int i = 0; i < 64; i ++) {
        if (n >> i & 1) {
            int x = (1ll << i);
            if (x != n) {
                cout << (n ^ x) << '\n';
                return ;
            }
        }
    }
    cout << -1 << '\n';
}

H.Instructions Substring

思路:首先特判目标点在 0 , 0 {0, 0} 0,0的情况,答案就是 n ⋅ ( n + 1 ) 2 \frac{n \cdot (n + 1)}{2} 2n(n+1),剩下的情况我一边遍历一边记录在这个位置我的坐标偏移到了那个位置,我把和目标点的位置差多少算出来,而这个差值我要让我减去前缀的贡献,记得要把这个前缀清零不然后续会重复计算

void solve() {
    int n, x, y; cin >> n >> x >> y;
    string s; cin >> s;
    int X = 0, Y = 0;
    map<pair<int, int>, int> mp;
    if (x == 0 && y == 0) {
        cout << n * (n + 1) / 2 << '\n';
        return ;
    }
    int ans = 0;
    mp[ {0, 0}] ++;
    for (int i = 0; i < n; i ++) {
        if (s[i] == 'A') X --;
        else if (s[i] == 'D') X ++;
        else if (s[i] == 'W') Y ++;
        else Y --;
        int dx = X - x, dy = Y - y;
        ans += mp[ {dx, dy}] * (n - i);
        mp[ {dx, dy}] = 0;
        mp[ {X, Y}] ++;
    }
    cout << ans << '\n';
}

I.Red Playing Cards

思路:其实感觉这个题更像线性的 d p dp dp f [ i ] f[i] f[i]表示进行删除操作以后的最大价值是多少,因为只有首位一样才可以删,所以对于每一对首位相同的区间我们进行 d p dp dp d p [ i ] dp[i] dp[i]表示到第 i i i个位置我的最大价值是多少,只有当我的一个区间完全包含在现在遍历的区间之内的时候,我才可能会考虑删还是不删,取 m a x max max即可

void solve() {
    int n; cin >> n;
    vector<int> a(n * 2  + 2);
    unordered_map<int, array<int, 2>> mp;
    for (int i = 1; i <= n * 2; i ++) {
        cin >> a[i];
        if (!mp.count(a[i])) {
            mp[a[i]][0] = i;
        } else {
            mp[a[i]][1] = i;
        }
    }
    vector<array<int, 3>> seg;
    for (auto [x, y] : mp) {
        seg.push_back({x, y[0], y[1]});
    }
    seg.push_back({0, 0, 2 * n + 1});
    sort(seg.begin(), seg.end(), [&](array<int, 3> a, array<int, 3> b) {
        return (a[2] - a[1]) < (b[2] - b[1]);
    });
    vector<int> f(n + 1);
    for (auto [val, l, r] : seg) {
        vector<int> dp(r + 1, 0);
        dp[l] = val;
        for (int i = l + 1; i <= r; i ++) {
            int lo = mp[a[i]][0], hi = mp[a[i]][1];
            dp[i] = max(dp[i], dp[i - 1] + val);
            if (lo > l && hi < r && i >= hi) dp[i] = max(dp[i], dp[lo - 1] + f[a[i]]);
        }
        f[val] = dp[r];
    }
    cout << f[0] << '\n';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值