Codeforces Global Round A-F1

A - Log Chopping

容易发现多个数和 1 1 1个数的操作次数为求和关系。于是考虑一个数的情况,显然与 x − 1 x - 1 x1操作次数的奇偶性有关。那么对所有数统计操作次数判断奇偶性即可。

#include 
#define int long long
using namespace std;

inline void solve(){
    int n; cin >> n;
    int cnt = 0;
    for(int i = 1; i <= n; i++){
        int num ; cin >> num;
        cnt += num - 1;
    }
    if(cnt & 1) cout << "errorgorn\n";
    else cout<<"maomao90\n";
}

signed main(){
    int t = 0; cin >> t;
    while(t--) solve();
    return 0;
}

B - I love AAAB

分类讨论题:

  • 对长度 1 1 1一定非法
  • 结尾不是 B B B或开头是 B B B一定非法
  • 对于一段前缀, B B B的数量一定不大于 A A A
#include 
#define int long long
using namespace std;

inline void solve(){
    string s; cin >> s;
    int cnt1 = 0, cnt2 = 0;
    if(s.size() == 1 || s.back() != 'B' || s[0] == 'B'){
        cout << "NO\n";
        return;
    }
    for(auto ch : s){
        if(ch == 'A') cnt1++; else cnt2++;
        if(cnt1 - cnt2 < 0){
            cout << "NO\n";
            return;
        }
    }
    cout << "YES\n";
}

signed main(){
    int t = 0; cin >> t;
    while(t--) solve();
    return 0;
}

C - Unequal Array

可以发现一个性质,如果要保证equality小于等于 1 1 1,操作的下标序列一定连续,否则至少产生两组相同的数。

那么我们直接从左右端点向中间找到需要修改的最短区间即可。

#include 
#define int long long
using namespace std;

const int N = 2e5 + 10;
int a[N];

inline void solve(){
    int n = 0; cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    int l = 1, r = n;
    while(l < n && a[l] != a[l + 1]) l++; l++;
    while(r > 1 && a[r] != a[r - 1]) r--; r--;
    if(l > r) cout << "0\n";
    else cout << max(r - l, 1ll) << '\n';
}

signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    int t = 0; cin >> t;
    while(t--) solve();
    return 0;
}

D - Cyclic Rotation

首先处理出 A A A中每个数字下一次出现的位置,然后两个指针从头开始匹配,当碰到不相等的时候对端点数字打标记,并倾斜对齐继续匹配,直至碰到与之前端点相同的数字,那么该段序列可以通过循环左移得到。如果碰到一个上下不匹配,而上方元素无后继的情况一定非法。

#include 
#define int long long
using namespace std;

const int N = 2e5 + 10, INF = 1e9;
int a[N], b[N], last[N], nxt[N];

map<int, int> mp;

inline void solve(){
    mp.clear();
    int n = 0; cin >> n;
    memset(last, 0, sizeof(int) * (n + 10));
    memset(nxt, 0, sizeof(int) * (n + 10));
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) cin >> b[i];
    for(int i = n; i; i--){
        if(last[a[i]]) nxt[i] = last[a[i]];
        last[a[i]] = i;
    }
    bool flag = true;
    for(int l = 1, r = 1;;){
        if(r > n) break;   
        else if(a[l] != b[r]){
            if(nxt[l]) mp[a[l]]++, l++;
            else{
                flag = false;
                break;
            }
        } else if(a[l] == b[r]){
            if(mp.count(b[r]) && mp[b[r]]) mp[b[r]]--;
            else l++;
            r++;
        }
    }
    if(!flag) cout << "NO\n";
    else cout << "YES\n";

}

signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    int t = 0; cin >> t;
    while(t--) 
    solve();
    return 0;
}

E - notepad.exe

首先,我们可以二分询问,得到所有单词在一行显示的宽度 W W W,最多需要 30 30 30次可得。然后我们枚举两行、三行 … n \dots n n行显示单词的高度 h h h,取面积 m i n min min即可。注意如果回答 h = 0 h = 0 h=0时代表放不下最长单词,此时直接break掉即可。

#include 
#define int long long
using namespace std;

const int N = 2e5 + 10, INF = 1e9;
int a[N];

int ask(int ans){
    cout << "? " << ans << endl;
    cin >> ans;
    return ans;
}

inline void solve(){
    int n = 0; cin >> n;
    int l = 1, r = INF, ans = 0;
    while(l <= r){
        int mid = l + r >> 1;
        if(ask(mid) == 1) r = mid - 1;
        else l = mid + 1;
    }
    ans = l;
    for(int i = 2; i <= n; i++){
        int tmp = ask(l / i);
        if(!tmp) break;
        else ans = min(ans, (l / i) * tmp);
    }
    cout << "! " << ans << endl;
}

signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    //int t = 0; cin >> t;
    //while(t--) 
    solve();
    return 0;
}

F1 - Array Shuffling

要求每次选择两个数列元素交换,使原始序列 A A A变为目标序列 B B B。现在给定 A A A,构造一个 B B B,使操作次数最大。

首先我们对于原始序列和目标序列,对每个元素从最终位置 i i i向初始位置 j j j建边。例如:

A = [1,2,3,1,2,3]
B = [3,1,2,2,3,1]

以上为例,我们可以取每个数字的交换后的最近位置建图如下:

Codeforces Global Round A-F1_第1张图片

这样的环将原序列分成了若干段,每段可以独立交换,每段交换的操作数=环的长度 − 1 -1 1

当然我们可以取交换后相对更远的位置(也就是更大的环),但是这样的情况一定更劣,因为在环总长度固定的前提下,大环 − 1 -1 1的更少。

那么我们的策略就很明显了,将给定的 A A A分隔成若干个尽可能大而没有重复元素的子序列,对于每个环循环移位一次即可。

#include 
#define endl '\n'
using namespace std;

const int N = 3e6 + 10;
int a[N], b[N];

inline void solve(){
    int n = 0, cnt = 0; cin >> n;    
    unordered_map<int, queue<int>> g;
    for(int i = 1; i <= n; i++) cin >> a[i], g[a[i]].emplace(i);
    while(cnt < n){
        vector<int> pos; 
        for(auto it = g.begin(); it != g.end(); ){
            if((it -> second).size() == 0){
                auto del = it++;
                g.erase(del);
                continue;
            }
            if((it -> second).size()){
                pos.emplace_back((it -> second).front());
                (it -> second).pop();
                cnt++, it++;
            }
        }
        for(int i = 0; i < pos.size(); i++){
            b[pos[i]] = a[pos[(i + 1) % pos.size()]];
        }
    }
    for(int i = 1; i <= n; i++) cout << b[i] << " \n"[i == n];
}

signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    int t = 0; cin >> t;
    while(t--) solve();
    return 0;
}

H.Zigu Zagu

大受震撼

没看懂,待补。

#include 
#define endl '\n'
using namespace std;

const int N = 3e6 + 10;
char s[N];
int sum0[N], sum1[N];

inline void solve(){
    int n = 0, m = 0; cin >> n >> m >> s + 1;
    for(int i = 1;s[i];++i) {
        sum0[i] = sum0[i - 1] + (s[i] == '0' && s[i] == s[i - 1]);
        sum1[i] = sum1[i - 1] + (s[i] == '1' && s[i] == s[i - 1]);
    }
    while(m--) {
        int l, r; cin >> l >> r;
        cout << max(sum0[r] - sum0[l], sum1[r] - sum1[l]) + 1 << endl;
    }
}

signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    solve();
    return 0;
}

你可能感兴趣的:(比赛题解,codeforces,算法,c++)