Codeforces Round #629 (Div. 3)

Codeforces Round #629 (Div. 3)

B. K-th Beautiful String

题意: 给定一个长度n,一个次序m,定义一个特殊字符串s为:s中只含有a和b,且b的个数为2。现在要求大于长度为n的,次序为m的字符串。

题解: 规律题。观察规律可以知道:

bb      .... 1

bab     .... 2
bba     .... 3

baab    .... 4
baba    .... 5
bbaa    .... 6

baaab   .... 7
baaba   .... 8
babaa   .... 9
bbaaa   .... 10

从上面可以看出,长度为2的有1个,长度为3的有2个,长度为4的有3个,那么第一个b的位置,从右往左数(从0开始计数)就是长度值,记为first,第二个b的位置就是first+1+m-sum[first],sum数组为每组个数的前缀和

代码:

#include <bits/stdc++.h>

#define int long long
using namespace std;

int const MAXN = 5e5 + 10;
int n, m, T, sum[MAXN];
char ans[MAXN];

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> T;
    for (int i = 1; i <= 5e5; ++i) sum[i] = sum[i - 1] + i;
    while(T--) {
        cin >> n >> m;
        for (int i = 0; i < n; ++i) ans[i] = 'a';
        for (int i = 1; i <= 5e5; ++i) {
            if (m - sum[i] <= 0) {
                ans[n - i - 1] = 'b';
                ans[n - i + sum[i] - m] = 'b';
                break;
            }
        }
        for (int i = 0; i < n; ++i) cout << ans[i];
        cout << endl;
    }
    return 0;
}

D. Carousel

题意: 给定n个数字,这n个数字首尾相连。现在要求给这些数字染色,要求相邻的不同数字必须染上不同的颜色,问最少需要多少颜色才能够将所有数字填满颜色。

题解: 最多的颜色只需要3种。

如果全部动物的类型都一样,那么1种颜色。

如果动物的类型>=2, 那么如果没有相邻的同色动物,且长度为偶数,那么1212填即可。

如果动物的类型>=2,那么如果有相邻的同色动物,且长度为奇数,我也填1212,然后找到一个相邻的动物的pos ~ n开始反转颜色。

如果动物的类型>=2,那么如果没有相邻的同色动物,且长度为奇数,那么前面也是1212,最后一个填为3.

代码:

#include <bits/stdc++.h>
using namespace std;

int attr[200050], res[200050];
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        int n, i, p = 1, type = 1, flg = 0;
        cin >> n >> attr[1];
        res[1] = 1;              //第一只动物直接处理成颜色1
        for (i = 2; i <= n; i++)  {
            cin >> attr[i];
            if (attr[i] != attr[i - 1]) {
                type = 2; //出现不同种类动物,种类数先变为2
                p = 3 - p;
                res[i] = p;
            }
            else {
                flg = i; //记录相邻种类相同的位置
                res[i] = p;
            }
        }
        if (n >= 3 && attr[1] != attr[n] && res[1] == res[n]) {
            if (flg) {
                for (i = flg; i <= n; i++)
                    res[i] = 3 - res[i]; //flg其后全部翻转即可
            }
            else {
                type = 3;
                res[n] = 3;
            }
        }
        cout << type << '\n';
        for (i = 1; i <= n; i++) cout << res[i] << ' ';
        cout << '\n';
    }
    return 0;
}

E. Tree Queries

题意: 给定一颗有n个节点的树,然后有m个询问,每次询问给定一个长度为t的数组,判断是否存在一条路径,使得路径上的点要不然位于数组中,要不然距离数组中的某个点距离为1.

题解: dfs+思维。对于路径中的每个点,其父节点必然存在于路径中,因此我们可以把所有的点均转换为父节点。那么我们只需要判断这些父节点是否均位于路径中即可,如果均位于路径中,那么必然存在路径。那么现在如果判断所有点是否位于一条路径上,我们可以维护每个点深度,然后按照深度排序,判断深度大的点是否在深度小的点的子树中即可,判断是否位于子树只需要使用dfs序就能够判断。

代码:

#include <bits/stdc++.h>

#define int long long
using namespace std;

int const MAXN = 2e5 + 10, MAXM = MAXN * 2;
int n, m, T;
int e[MAXM], ne[MAXM], h[MAXN], idx, dfn[MAXN], tot, ed[MAXN];
int father[MAXN], dep[MAXN];

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u, int fa) {
    dfn[u] = ++tot;  // dfn[i]=j表示i的dfs序为j
    father[u] = fa;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa) continue;
        dep[j] = dep[u] + 1;
        dfs(j, u);
    }
    ed[u] = tot;  // ed[i]=j表示i的子树的最后一个节点的dfs序为j
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) h[i] = -1;
    for (int i = 1; i <= n - 1; ++i) {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);
    }
    dfs(1, -1);
    for (int i = 1, k; i <= m; ++i) {
        cin >> k;
        vector<pair<int, int> > path;
        for (int j = 1, t; j <= k; ++j) {
            cin >> t;
            if (t != 1) t = father[t];
            else t = 1;
            path.push_back({dep[t], t});
        }
        sort(path.begin(), path.end());
        reverse(path.begin(), path.end());
        int flg = 1;
        for (int i = 0; i < path.size() -1 ; ++i) {
            if (dfn[path[i].second] < dfn[path[i + 1].second] || dfn[path[i].second] > ed[path[i + 1].second]) {
                flg = 0;
                break;
            }
        }
        if (flg) cout << "YES\n";
        else cout << "NO\n";
    }
    return 0;
}

F. Make k Equal

题意: 给一个 n 和 k,以及长度为 n 的序列 a。

有两种操作:

  • 让数组中其中一个最小值的值加一。
  • 让数组中其中一个最大值的值减一。

问:最少几次操作,可以使得数组中至少有 k 个数相等。 1 < = k , n < = 2 ∗ 1 0 5 , 1 < = a i < = 1 0 9 1<=k,n<=2*10^5,1<=a_i<=10^9 1<=k,n<=2105,1<=ai<=109

题解: 必定存在最优解,满足最后相等的 k 个数的值,为 a 中出现过的值。那么我们就可以排序后,直接枚举最后出现的值。对于当前的值a[i],如果比它小的有k个,那么我们可以将比他小的值变为a[i],这样修改的次数就是A = i * a[i] - s[i] - (i - k);如果比它大的有k个,还可以把比他大的值变为a[i],这样修改次数就是B = t[i] - (n - i + 1) * a[i] - (n - i + 1 - k);如果比他小或者比他大的不足k个,那么我们既需要把比他小的,也需要把比它大的变为a[i],这样修改次数为A + B - k + 1。枚举到当前的a[i]时,直接枚举这三种情况,取个min即可。

代码:

#include <bits/stdc++.h>

using namespace std;

#define int long long
const int MAXN = 200002;
int n, k, a[MAXN], s[MAXN], t[MAXN], c[MAXN], r = 1e18, A, B;

signed main() {
    scanf("%lld%lld", &n, &k);
    for (int i = 1; i <= n; ++i) scanf("%lld", a + i);
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; ++i) s[i] = s[i - 1] + a[i];
    for (int i = n; i >= 1; --i) t[i] = t[i + 1] + a[i];
    for (int i = 1; i <= n; ++i) {
        if (a[i] == a[i - 1])
            c[i] = c[i - 1] + 1;
        else
            c[i] = 1;
        if (c[i] >= k) {
            puts("0");
            return 0;
        }
    }
    for (int i = 1; i <= n; ++i) {
        A = i * a[i] - s[i] - (i - k);
        B = t[i] - (n - i + 1) * a[i] - (n - i + 1 - k);
        if (i >= k) r = min(r, A);
        if (n - i + 1 >= k) r = min(r, B);
        r = min(r, A + B - k + 1);
    }
    printf("%llu\n", r);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值