Codeforces Round #764 (Div. 3)

题目链接

纪念第一次补全cf:
在这里插入图片描述

在这里插入图片描述
感动~~

A. Plus One on the Subset

题目大意:给出一个数组,每次我们可以选几个数+1,问至少多少次使得该数组中所有数大小相等。
签到
输出最大值 - 最小值即可, 因为对于每个数每次我们都只能使其增加1.

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

int main(void)
{
    int  T;
    scanf("%d", &T);
    while(T --)
    {
        int n;
        scanf("%d", &n);
        vector<int> vec(n);
        int maxn = 0, minn = 1e9 + 1;
        for(int i = 0; i < n; i ++)
        {
            scanf("%d", &vec[i]);
            if(vec[i] > maxn) maxn = vec[i];
            if(vec[i] < minn) minn = vec[i];
        }
        printf("%d\n", maxn - minn);
    }
    return 0;
}

B. Make AP

题目大意:
给出三个数a, b, c, 可以进行一次操作使得其中一个数 * m, 能否找到这样的一个m,使得a, b, c为等差数列。

等差公式

分析:
等差公式: a + c = 2 * b
分类讨论扩大a, c或者b即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int arr[20];
int main(void)
{
    int  T;
    scanf("%d", &T);
    while(T --)
    {
       int a, b, c;
       cin >> a >> b >> c;
       bool flag = false;

       int temp = (2 * b) - c;
       if((a + c) % (2 * b) == 0) flag = true;

       if(temp > 0 && temp % a == 0) flag = true;
       temp = (2 * b) - a;

       if(temp > 0 && temp % c == 0) flag = true;
       if(flag) puts("YES");
       else puts("NO");
    }
    return 0;
}

C. Division by Two and Permutation

题目大意:
给出n个数,可以使其中的数除以2(向下取整),可以进行无数次该操作。问能否得到一个1-n的全排列。

贪心

分析

  1. 若当前值 > n, 让其不断除以2, 找到第一个未标记的1-n的值,标记上。若无解,最后该值不断除以2会得0, 切忌死循环 (悲痛wa)
  2. 切记找未标记的下一个值。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int main(void)
{
    int  T;
    scanf("%d", &T);
    while(T --)
    {
       int n;
       scanf("%d", &n);

       map<int, int> mp;
       int maxn = 0;
       for(int i = 0; i < n; i ++)
       {
           int x;
           scanf("%d", &x);
           while(x > n){
            x /= 2;
           }
           while(mp[x] && x) x /= 2;
           mp[x] ++;
       }

       bool flag = true;
       for(int i = 1; i <= n; i ++)
       {
           if(mp[i] == 0) {
            flag = false;
            break;
           }
       }
       if(flag) puts("YES");
       else puts("NO");
    }
    return 0;
}


D. Palindromes Coloring

题目大意:给出一个字符串和k种颜色,要求每种颜色至少要有一个字母填涂,涂色完毕后,填涂同一种颜色的字符可以重新排列,从而组成一个新的字符串(要求必须是一个回文串),问,得到的k个回文字符串长度最小的那个长度最大是多少?

思维、贪心

分析

  1. 回文串性质:
    (1)一个回文串是偶数是,每个字母出现的次数均为偶数。如aabbcc, acbbca
    (2)如果是奇数,有一个字母出现了的次数为奇数,位于中间 : acbbbca

  2. 每个字母按照对数分配给每个字符串,这样组成的字符串是偶数的,可以构成回文串; 再看看剩下的未分配的字母(本来为奇数的字母 + 未分配给字符串的成对的字母), 若有>=k个,则可以给每一个字符串一个(放到中间, 如acbca)。

比赛的时候想到了分奇偶,但没想到成对分配。还是要考虑回文串的性质的
【以后遇到该种类型的题,先思考隐藏的性质比较好】

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

int main(void)
{
    int  T;
    scanf("%d", &T);
    while(T --)
    {
        int n, k;
        scanf("%d%d", &n, &k);
        string s;
        cin >> s;
        int st[30];
        memset(st, 0, sizeof st);
        int cnt = 0;
        for(int i = 0; i < s.size(); i ++)
        {
            st[s[i] - 'a'] ++;
        }
        int ans = 0, t = 0;
        for(int i = 0; i < 26; i ++)
        {
            if(st[i] % 2) t ++;
            ans += st[i] / 2; //多少对
        }
        t += 2 * (ans % k);
        ans /= k;
        if(t >= k) ans = ans * 2 + 1;
        else ans *= 2;
        cout << ans << endl;
    }
    return 0;
}

E. Masha-forgetful

题目大意:
给出n个字符串,和另一个字符串s。问s能否用前n个字符串的子串表示出来(要求字串长度>=2).。可以的话按照指定格式输出,不可以输出-1。

分析

  1. 前n个字符串分成2个字符和三个字符起来,然后分析s能否由这些小段组成
    (1)合理性:若s中长度为奇数的子串是某个字符串的子串, 可以将该奇数分成 2 * a + 3 * b ,即a个两个字母的小子串 + b个三个字母的小子串; 若长度为偶数,可以分成a 个两个字母的子串。
    (2)最优性:s中长度越小的子串 是 前n个串的子串的概率越大。

  2. 存储我们用vector, 存完之后如何判断能否拼成s?
    我们这里运用了dp的思想。
    (1)状态表示:dp[i]表示 i 前面这段的起点信息, 当dp[i]!= -1时, s 中 i 之前的字符都是可以得到的。
    (2)状态转移:则当dp[i + 1 - 2]存在且合法时(即前i - 1个字符都能懂n个字符串中得到),前n个字符串能组成s[i-1] - s[i]时,我们状态可以由其转移过来。也就是说i + 1之前的这小段是可以得到的起点是 i - 1。dp[i + 1] = i - 1;
    dp[i-3]同理。
    此时处理dp[i]存储的是以i - 1结尾的这段小段在前n个字符串中的信息:l,r以及在第几个字符串中。
    (3)若dp[m] != -1, 则m之前的都可以得到,s是可以得到的。

  3. 输出答案时,我们需要从后往前推,dp[m]固定,推出前面小段的信息,存储在答案数组中,因此,输出答案时,需要翻转一下,从前往后输出。

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

void solve()
{
    int n, m, i, j;
    vector<int> num_2[10][10];
    vector<int> num_3[10][10][10];
    cin >> n >> m;
    string s;
    for(int i = 0; i < n; i ++)
    {
        cin >> s;
        for(int j = 0; j < m - 1; j ++)
        {
            num_2[s[j] - '0'][s[j + 1] - '0'] = {j + 1, j + 2, i + 1};
        }
        for(int j = 0; j + 2 < m; j ++)
        {
            num_3[s[j]-'0'][s[j + 1] - '0'][s[j+2]-'0'] = {j + 1, j + 3, i + 1};
        }
    }
    cin >> s;
    vector<int> dp(m+1, -1);
    dp[0] = 0;
    for(int i = 1; i < m; i ++)
    {
        if(num_2[s[i - 1] - '0'][s[i] - '0'].size() && dp[i + 1 - 2] != -1)
        {
            dp[i + 1] = i - 1;
        }
        if(i >= 2)
        {
            if(num_3[s[i - 2]-'0'][s[i - 1] - '0'][s[i] - '0'].size() && dp[i+1-3] != -1){
                dp[i + 1] = i - 2;
            }
        }
    }
    if(dp[m] == -1)
    {
        puts("-1");
        return ;
    }
    i = m;
    vector<vector<int>>ans;
    while(i > 0)
    {
        if(dp[i] == i - 3)
        {
            ans.push_back(num_3[s[i - 1 - 2] - '0'][s[i - 1 - 1] - '0'][s[i - 1]-'0']);
            i -= 3;
        }
        else{
            ans.push_back(num_2[s[i - 1 - 1]-'0'][s[i - 1] - '0']);
            i -= 2;
        }
    }
    reverse(ans.begin(), ans.end());
    cout << ans.size() << endl;
    for(auto &it: ans)
    {
        cout << it[0] << " " << it[1] << " " << it[2] <<endl;
    }
}
int main(void)
{
    int  T;
    scanf("%d", &T);
    while(T --)
    {
        solve();
    }
    return 0;
}


F. Interacdive Problem

题目大意:给出一个x (1 <= x < n)和 n, 你可以询问 + c, 代表 x = x + c, 会返回给 x / n(下取整)。
你可以询问至多10次,输出当前这个x是多少。
二分、思维、交互题

分析

  1. 当x < n时, x / n 是0;
    当 x >= n 且 x < 2n时,返回的是1;
    当 x >= 2n 且 x < 3n时,返回的时2
    因此具有单调性。可以用二分.

  2. 对于中间值mid, 当前期望加的值为 n - mid % n, 这样得到的期望值答案是:mid / n + 1。 当满足这个条件时,x位于[mid, r]。 不满足时,x位于[l, mid - 1]。我们每次询问都会给x加上一个值,l,r相应的加上该值即可。n不变。

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

int main(void)
{
    int n;
    cin >> n;
    int l = 1, r = n - 1;

    while(l < r)
    {
        int mid = l + r + 1>> 1;
        int x = n - mid % n;
        int y = mid / n + 1;
        cout << "+ " << x << endl;
        int ans;
        cin >> ans;
        if(ans == y) l = mid;
        else r = mid - 1;
        l += x, r += x;

    }
    cout << "! " << l << endl;
    return 0;
}


G. MinOr Tree

题目大意:
给出一个n个点m条边的图,要求剩下n-1条边,使得该图连通,并且各个边的按位或得到的值尽可能小,求该最小值。(最小或树)

贪心、位 优化

思路:

  1. 答案不会超过2 ^ 30次方, 二进制从高位枚举,若该位取0,仍能构成连通图则取0, 如不能则取1.得到的答案我们记为res。
    当时看到这种解法是,我在想,能否满足他是n-1条边的限制。试想,若能去掉一些边使得res更小,在按位枚举时就会去掉,因此剩下的边一定能够去掉一些无关res大小的边,并且使得剩下的n-1为一个连通子图。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200005;
int n, m, res;
struct node{
    int a, b, w;
}e[N];
int p[N];

int find(int x)
{
    return x != p[x] ? p[x] = find(p[x]) : x;
}

bool check(int k)
{
    for(int x = 1; x <= n; x ++) p[x] = x;
    for(int i = 0; i < m; i ++)
    {
        if(((e[i].w | res) >> k) == (res >> k)){
            p[find(e[i].a)] = find(e[i].b);
        }
    }
    int cnt = 0;
    for(int x = 1; x <= n; x ++)
    {
        if(x == p[x])
        {
            cnt ++;
        }
    }
    return cnt > 1;
}
int main(void)
{
    int T;
    scanf("%d", &T);
    while(T --)
    {
        scanf("%d%d", &n, &m);
        for(int i = 0; i < m; i ++)
        {
            scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].w);
        }
        res = 0;
        for(int k = 29; k != -1; k --)
        {
            if(check(k)) res |= (1 << k);
        }
        printf("%d\n", res);
    }
    return 0;
}


这次cf又掉分了,嘤嘤嘤。纵使虐我千百次,我仍待它如初恋。下次可上点分吧…

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xuhx&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值