Codeforces Round #739 (Div. 3) A-F

本文介绍了四道算法竞赛题目,涉及数论、环形结构、搜索和字符串处理等知识点。通过预处理、二分查找、贪心策略等方法,解析了每道题目的解题思路,并提供了C++实现。此外,还探讨了一种复杂情况的解决策略,即通过模拟删除字符的过程来还原原始字符串和删除顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A题

  • 问不能被3整除且最低位不是3的第kkk大数,打表预处理即可
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int Data[MAXN];
int main(){
    #ifdef LOCAL
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
    #endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    int len = 0;
    cin >> t;
    for(int i=1;i<=10000;i++){
        if(i % 10 == 3 || i % 3 == 0) continue;
        Data[++len] = i;
    }
    while(t--){
        int n;
        cin >> n;
        cout << Data[n] << '\n';
    }
    return 0;
}

B题

  • 显然,如果两个数是oppositeoppositeopposite的,那么他们差的绝对值应该是整个环长度的一半,现在问cccoppositeoppositeopposite是谁,那么也应该相差环长的一半,所以只需要看ccc和环长一半之间的关系即可,小于就加上,大于就减去,最后看a,b,ca,b,ca,b,c是否超过环长,如果是则无解
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int Data[MAXN];
int main(){
    #ifdef LOCAL
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
    #endif
    ios::sync_with_stdio(false);
    int t;
    int a, b, c;
    cin >> t;
    while(t--){
        cin >> a >> b >> c;
        if(a > b) swap(a, b);
        int p = b - a;
        int circle = (p << 1);
        if(c > circle){
            cout << -1 << '\n';
        }else{
            if(c > p) c -= p;
            else c += p;
            if(a > circle || b > circle || c > circle) cout << -1 << '\n';
            else cout << c << '\n';
        }
    }
    return 0;
}

C题

  • 先预处理出iii的平方
  • 显然左上角的正方形区域每一个都是i2i^2i2,接下来只需要二分查找给出的kkk应该处于哪个环状区域内,如果找到发现它等于某个平方项,那么他应在正方形区域的左下角,否则只需要分别向下和左寻找即可
    在这里插入图片描述
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
ll Data[MAXN];
ll MAX = 1e10;
int main(){
    #ifdef LOCAL
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
    #endif
    ios::sync_with_stdio(false);
    int t;
    ll k;
    cin >> t;
    int len = 1;
    for(ll i=1;;i++){
        Data[len++] = i * i;
        if(i * i > MAX) break;
    }
    while(t--){
        cin >> k;
        int p = lower_bound(Data + 1, Data + len + 1, k) - Data;
        if(Data[p] == k){
            cout << p << " " << 1 << '\n';
        }else{
            --p;
            k -= Data[p];
            bool f = false;
            for(int i=1;i<=p+1;i++){
                if(k == i){
                    f = true;
                    cout << i << " " << p + 1 << '\n';
                    break;
                }
            }
            if(f) continue;
            k -= p + 1;
            for(int i=1;i<=p;i++){
                if(k == i){
                    f = true;
                    cout << p + 1 << " " << p - i + 1 << '\n';
                    break;
                }
            }
        }
    }
    return 0;
}

D题

给出一个数字nnn,可以删除其任意一位的数字,也可在其最右侧填充任意数字,问最少需要多少步能够使得这个数字变成222的若干次方

  • 枚举每一个2i(0≤i≤63)2^i(0\leq i\leq63)2i(0i63),分别求最少的步骤数量再取最小值,每次枚举贪心考虑,因为是改nnn,所以找nnn的子序列里面能够等于的,2i2^i2i的最长前缀,这样只需要把nnn里其余的部分都删掉,2i2^i2i的其余部分都加上,即为当前枚举的数字的最小值
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
ll Data[MAXN];
string b[MAXN];
int main(){
    #ifdef LOCAL
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
    #endif
    ios::sync_with_stdio(false);
    int t;
    Data[0] = 1;
    b[0] = "1";
    for(int i=1;i<=63;i++){
        Data[i] = Data[i - 1] * 2;
        b[i] = to_string(Data[i]);
    }
    string s;
    cin >> t;
    while(t--){
        cin >> s;
        int i = 0;
        int j = 0;
        int len = s.length();
        int ans = INF;
        for(int i=0;i<=63;i++){
            int num = 0;
            int sz = b[i].length();
            int k = 0;
            for(int j=0;j<len;j++){
                if(s[j] == b[i][k]){
                    k++;
                }
            }
            num = len - k;
            num += sz - k;
            ans = min(ans, num);
        }
        cout << ans << '\n';
    }    
    return 0;
}

E题

给你一个字符串,现在删去其任意一个字母,将得到的结果加在其后面,然后将刚才得到的那个删除字母之后的字符串再次删去任意一个字母,进行同样操作,知道字母全部操作完成,现在给你最后的结果,问原字符串和删去字母的顺序

  • 举个例子,如果原来的字符串是abacabaabacabaabacaba,那么现在先删除bbb,得到aacaaaacaaaacaa,将它加在原来字符串的后面,得到abacabaaacaaabacabaaacaaabacabaaacaa,再将aacaaaacaaaacaa删去aaa,得到ccc,加在后面得到abacabaaacaaacabacabaaacaaacabacabaaacaaac,现在给你abacabaaacaaabacabaaacaaabacabaaacaa,问原来的那个字符串和删除顺序是什么,答案应该是abacabaabacabaabacababacbacbac
  • 可以发现,每个字母都要删除,那么如果从后往前看,第几次出现的字母就应该是倒数第几次被删除的。这样可以得到删除字母的顺序,然后把被删除的字母按顺序整理出来,接下来开始找原串
  • 容易理解,第一次被删除的字符在最后得到的串中出现的次数即为它在原串中出现的次数,第二次被删除的字符在最后得到的串中出现的次数即为它在原串中出现次数的两倍,以此类推,那么只需要枚举被删除的字母,就可以得到原来串的长度(记录每个字母的出现次数,然后分别除以它们被删除的顺序数,最后累加即可得到),那么只需要在最后得到的串中从前到后截取这段长度即为原来的字符串
  • 最后将这个字符串按照题目要求跑一遍,如果得到的最终字符串是要求的即为有解,否则无解,这里也需要一些编程的技巧(参考标程代码,删除一遍之后赋值回去继续删除,使用autoautoauto关键字会比较方便,具体参考代码如下)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int Data[MAXN];
int main(){
    #ifdef LOCAL
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
    #endif
    ios::sync_with_stdio(false);
    int t;
    string s;
    cin >> t;
    while(t--){
        cin >> s;
        int len = s.length();
        reverse(s.begin(), s.end());
        unordered_map<char, int> mp;
        string ans2;
        for(int i=0;i<len;i++){
            if(!mp.count(s[i])){
                ans2.push_back(s[i]);
            }
            mp[s[i]] += 1;
        }
        reverse(ans2.begin(), ans2.end());
        int b = ans2.length();
        len = 0;
        for(int i=0;i<b;i++){
            len += mp[ans2[i]] / (i + 1);
        }
        string ans1 = string(s.rbegin(), s.rbegin() + len);
        string result = ans1;
        string ss = ans1;
        for(auto i : ans2){
            string temp;
            for(auto j : ss){
                if(i != j){
                    temp.push_back(j);
                    result.push_back(j);
                }
            }
            ss = temp;
        }
        reverse(s.begin(), s.end());
        if(result == s){
            cout << ans1 << " " << ans2 << '\n';
        }else cout << -1 << '\n';
    }
    return 0;
}

F题

给你数nnn和数kkk,问大于等于nnn的每位上的不相同的数字个数不超过kkk的最小数,比如n=177890,k=2n=177890,k=2n=177890,k=2,那么答案应该是181111181111181111

  • 看一下怎么推的,首先从左往右177177177没事,到888,发现现在不同数字的个数超过222了,怎么办呢,因为只能往大了找,不能减小,所以只能把它加一,当然如果这是999,那么就往前找,直到找到一个不是999的位置,把它加一,你要说前面要全都是999怎么办,这不可能,因为如果前面都是999,那么就都相同了,也就不存在这个问题了
  • 那么后面的数字该怎么办呢?,维持原状肯定不行,因为那这前面直到000的数字就都丢了,所以应该把这些数字都变成000,这样就不会丢解,这样一次一次的更新,直到符合要求
  • 因为最多也就是101010个不同的数字,这样不停的遍历,差不多几次就会确定一个位置的数字,不会超时
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
#include <set>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int Data[MAXN];
string solve(){
    string n;
    int k;
    cin >> n >> k;
    int len = n.length();
    set<char> st;
    while(1){
        st.clear();
        for(auto i : n){
            st.insert(i);
        }
        if(st.size() <= k) return n;
        st.clear();
        int ptr = 0;
        for(; ; ptr++){
            st.insert(n[ptr]);
            if(st.size() > k){
                while(n[ptr] == '9'){
                    --ptr;
                }
                n[ptr]++;
                for(int i=ptr+1;i<len;i++){
                    n[i] = '0';
                }
                break;
            }
        }
    }
    return n;
}
int main(){
    #ifdef LOCAL
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
    #endif
    ios::sync_with_stdio(false);
    int t;
    cin >> t;
    while(t--){
        cout << solve() << '\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Clarence Liu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值