Codeforces Round #710 (Div. 3) A B C D E

第一次过五道题目,但是起手慢了,相对于其他选手的优势也很小.最后和平时的排名百分比没什么区别.

E题的通过人数是三千,到了F题只剩500形成断崖,所以想突破一道百人题对我还是很难.

码量是有一点的(因为平时div2太弱最多写到三题),基本上是在面向AC做题,使用各种基本技巧就可以了,处理起来需要稍加思考.

做完E题差不多只有十分钟了,这个手速肯定欠练.

 

早上一看发现E被hack了,持续掉分.= =


 

A

草稿纸上面计算一下在Colum模式下数字x所处的第i行,第j列,然后计算出在rows模式下第i行,第j列的数字即可.

主要用到取模和向上取整的整除.

我花了接近20分钟,打草稿的效率太低了,一直出错.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

long long n, m, x;

void solve(){
    scanf("%lld%lld%lld", &n, &m, &x);
    long long i = x % n;
    long long j = (x - 1) / n + 1;
    if(!i) i = n;
    if(!j) j = m;

    printf("%lld\n", (i - 1) * m + j);
}

int main(){
    int t;
    scanf("%d", &t);
    while(t--) solve();   

    return 0;
}
A

 

B

数据保证有解.

按照题意模拟即可,先将第一个*改为x,之后维护上一个x的位置last,检测到 . 则跳过,检测到*且位置距离last大于k时,向前查找一个*满足其位置与last相差不大于k,将其改为x并更新last.

最后检查一下末端是否满足最后一个*改为了x.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;

int n, k;
string s;

void solve() {
    cin >> n >> k >> s;
    bool hd = false;
    int last = 0, ans = 0;
    for (int i = 0; i < s.size(); i++) {
        if(s[i] == '.') continue;
        if(!hd){
            s[i] = 'x';
            last = i;
            hd = true;
            ans++;
            continue;
        }
        while(i - last > k){
            int p = i;
            while(p - last > k || s[p] != '*') p--;
            last = p;
            s[p] = 'x';
            ans++;
        }
    }
    for(int i = s.size() - 1; i >= 0; i--)
        if(s[i] == 'x') break;
        else if(s[i] == '*'){
            ans++;
            break;
        }

    printf("%d\n", ans);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) solve();

    return 0;
}
B

 

C

数据范围惊人地小,那么可以O(N2)对比,找到两字符串能匹配的最大长度,然后让其保留此长度的字符串并删除其余字符,需要的总操作数显然是(a的长度)+(b的长度)-2*(匹配的最长字符串的长度).

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;

string a, b;

void solve() {
    cin >> a >> b;
    int big = 0;
    for (int i = 0; i < a.size(); i++) {
        for (int j = 0; j < b.size(); j++) {
            int ct = 0;
            int p1 = i, p2 = j;
            while (p1 < a.size() && p2 < b.size() && a[p1] == b[p2]) {
                ct++;
                p1++;
                p2++;
            }

            big = max(big, ct);
        }
    }
    printf("%d\n", a.size() + b.size() - 2 * big);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) solve();

    return 0;
}
C

 

D

应该是前五题里最麻烦的吧.

我维护了一个数组rkct[i],其值为rank为i的数字(也就是离散化)出现的次数.数组的顺序是无关紧要的,可以舍弃这个信息.

也不需要关系rank为i的数字是谁,所以干脆再把rkct数组也升序排列,看起来更好操作.

想到过几种方法都没找到出路:

  让rkct从两端相互抵消,不能处理112233,但可以处理11223333.

  让rkct不停从左到右,相邻项抵消,可以处理112233,但不能处理11223333.

发现这两种方法分别解决了一个问题,于是有所启发:

  ①从首个数字不停地顺序,循环取走,直至下一个要取的数字会与上一个相同.

    如11223333依次取出123123,剩下33.

    会发现这时剩下的数字只会是出现次数最多的数字.

  ②将剩下的数字插入到取出的序列.

    即将两个3插入到先前得到的123123中,这很容易做到,可以计算出可插入点(用0表示)010231023有三个.

最后仍然可能有数字剩下,这时就需要直接统计到答案里了.

注意,由于是两两消除,数组元素数量却会出现奇数情况,这只需要在最后处理答案时进行考虑.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;

int n, a[200010], rkct[200010];
vector<int> v;

void solve(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", a + i), rkct[i] = 0;
    sort(a + 1, a + n + 1);
    v.clear();

    int cur = a[1], ind = 1;
    for(int i = 1; i <= n; i++){
        if(a[i] == cur) rkct[ind]++;
        else cur = a[i], rkct[++ind]++;
    }
    if(ind == 1) {printf("%d\n", rkct[1]); return;}
    sort(rkct + 1, rkct + ind + 1);

    int l = 1;
    while(rkct[ind - 1]){
        for(int i = l; i <= ind; i++){
            rkct[i]--;
            v.push_back(i);
        }
        while(!rkct[l]) l++;
    }
    int spa = 0;
    for(int i = 0; i < v.size(); i++)
        if(v[i] != ind && (i == 0 || v[i - 1] != ind))
            spa++;

    if(rkct[ind] <= spa){
        printf("%d\n", (v.size() + rkct[ind]) % 2);
    }else{
        int ans = (v.size() + spa) % 2;
        printf("%d\n", ans + rkct[ind] - spa);
    }
    // for(int i = 1; i <= ind; i++) printf("_%d ", rkct[i]);
    // puts("");
}

int main(){
    // freopen("in.txt", "r", stdin);
    int t;
    scanf("%d", &t);
    while(t--) solve();   

    return 0;
}
D

 

E

对于数据给定的序列,若q[i]!=q[i-1],说明位置i一定为q[i].

依次可以确定出所有固定的点,剩下的未使用的点构成一个集合.

想要得到最小字典序很简单,从这个集合中不停取走最小者安排到空缺的位置即可.

 

别看这段↓

而想要得到最大字典序,对于未确定的位置i,插入的值范围为[1,q[i]-1],从q[i]-1一直递减,检查到第一个未使用值时将其插入并标记为已使用即可.

这个过程花费的时间复杂度我没有细算,但是TLE了.

加了了记忆化,数组big[x]表示小于x且可能 未使用的最大值,每当发现某个q[i]=x的点插入了值p,就将big[x]更新为p-1.

由于只是可能未使用,还是要不停向下检查直到找到确实未使用的值.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int n, q[200010], ans[200010];
int big[200010];
bool used[200010], tused[200010];

void solve() {
    memset(used, 0, sizeof(used));
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", q + i);
        big[q[i]] = q[i] - 1;
        ans[i] = 0;
        if (q[i] != q[i - 1]) {
            ans[i] = q[i];
            used[q[i]] = true;
        }
    }

    int p = 1;
    memcpy(tused, used, sizeof(used));
    for (int i = 1; i <= n; i++) {
        if (ans[i])
            printf("%d ", ans[i]);
        else {
            while (tused[p]) p++;
            tused[p] = true;
            printf("%d ", p);
        }
    }
    puts("");

    for(int i = 1; i <= n; i++){
        if(ans[i]) printf("%d ", ans[i]);
        else{
            int p = big[q[i]];
            while(used[p]) p--;
            used[p] = true;
            big[q[i]] = p - 1;
            printf("%d ", p);
        }
    }
    puts("");
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) solve();

    return 0;
}
E Hacked

 

醒醒,这个复杂度仍然是O(N2),你被hack了.

正确的求最大字典序方法是二分找值插入.

使用set,这个数据结构可以按值删除元素,也可以二分查找.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <set>
using namespace std;

int t, n;
int a[200010];
set<int> s;

void solve() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]), s.insert(i);

    for (int i = 1; i <= n; i++) {
        if (a[i] != a[i - 1])
            printf("%d ", a[i]), s.erase(a[i]);
        else
            printf("%d ", *s.begin()), s.erase(s.begin());
    }
    puts("");

    for (int i = 1; i <= n; i++) s.insert(i);
    for (int i = 1; i <= n; i++) {
        if (a[i] != a[i - 1])
            printf("%d ", a[i]), s.erase(a[i]);
        else {
            auto it = prev(s.lower_bound(a[i]));
            printf("%d ", *it);
            s.erase(it);
        }
    }
    puts("");
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) solve();

    return 0;
}
E

 

posted @ 2021-03-26 01:13  goverclock  阅读(77)  评论(0编辑  收藏  举报