【cf】CodeForces Round 905(Div.3)题解

A. Morning

原题链接

题意

一个人要打字,光标现在停在1,键盘顺序是1234567890,挪动一个位置和按下这个键都需要花费1s,问打完给定字符串最少需要几秒

思路

这题有个需要注意的点是,到0之后想打别的键是往左走,不可以往右直接到1,注意这个其他就没什么了

代码

#include 

using namespace std;

typedef pair<int, int> PII;

using i64 = long long;
typedef long long LL;

void solve()
{
    string s;
    cin >> s;
    i64 ans = 0;
    unordered_map<int, int> pos;
    for (int i = 1; i <= 9; i ++ ) pos[i] = i;
    pos[0] = 10;
    ans = abs(pos[s[0] - '0'] - pos[1]) + 1;
    for (int i = 1; i < s.size(); i ++ )
    {
        ans += abs(pos[s[i] - '0'] - pos[s[i - 1] - '0']) + 1;
    }
    cout << ans << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t;
    cin >> t;
    // t = 1;
    while (t -- )
    {
        solve();
    }
}

B. Chemistry

原题链接

题意

给定字符串,问能不能删掉k个字符,然后对整个字符串重新排列,使得新字符串对称

思路

不同字母个数都是偶数,或者只有一个字母个数是奇数的情况下,可以让整个字符串对称,所以判断一下删掉k个字符之后能不能使得奇数个数的字符小于等于1个就可以了

代码

#include 

using namespace std;

typedef pair<int, int> PII;

using i64 = long long;
typedef long long LL;

void solve()
{
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;

    unordered_map<char, int> cnt;
    for (int i = 0; i < n; i ++ ) cnt[s[i]] ++ ;

    bool flag = false;
    int idx = 0;
    for (auto t : cnt)
    {
        int m = t.second;
        if (m % 2 != 0) idx ++ ;
    }
    if (idx - k > 1) cout << "NO\n";
    else  cout << "YES\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t;
    cin >> t;
    // t = 1;
    while (t -- )
    {
        solve();
    }
}

C. Raspberries

原题链接

题意

给出一个数组,每次操作可以将任意一位加一,问最少操作多少次能让数组中所有元素的乘积是k的倍数

思路

因为k是2/3/4/5,235都是素数,所以只要数组中出现了他们的倍数即可,如果k是4,那么有两种情况,一种是有一个数是4的倍数,那么直接整除,另一种情况是有两个数是2的倍数,也可以让整体的乘积凑出4的倍数

代码

#include 

using namespace std;

typedef pair<int, int> PII;

const int mod = 998244353;

using i64 = long long;
typedef long long LL;

void solve()
{
    int n, k;
    cin >> n >> k;
    vector<int> a(n);
    int ans = k;
    int p = 0, cnt1 = 0, t = 5, cnt = 0;
    for (int i = 0; i < n; i ++ ) cin >> a[i];
    
    for (int i = 0; i < n; i ++ )
    {
        if (a[i] % k == 0)
        {
            cout << "0\n";
            return;
        }
        else
        {
            if (k != 4) t = k - (a[i] % k);
            else
            {
                if (a[i] % 2 == 0) cnt ++ ;
                else if ((a[i] + 1) % 4 == 0) cnt1 ++ ;
            }
        }
        ans = min(ans, t);
    }

    if (k != 4) cout << ans << '\n';
    else
    {
        if (cnt >= 2) cout << "0\n";
        else if (cnt1 != 0) cout << "1\n";
        else cout << 2 - cnt << '\n';
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t;
    cin >> t;
    // t = 1;
    while (t -- )
    {
        solve();
    }
}

D. In Love

原题链接

题意

给出多个子段的左右端点,问加入或删去当前字段后,还是否存在完全不重合的两个子段

思路

用multiset存(今天是长见识了qaq),可以存重复元素的set(默认升序),跑起来也会比map快

只要最大的开头元素小于最小的末尾元素,就可以说明有不重复的子段

代码

#include 

using namespace std;

typedef pair<int, int> PII;

const int mod = 998244353;

using i64 = long long;
typedef long long LL;

void solve()
{
    int q;
    cin >> q;
    multiset<int> a, b;
    while (q -- )
    {
        string op;
        int l, r;
        cin >> op >> l >> r;

        if (op[0] == '+')
        {
            a.insert(l);
            b.insert(r);
        }
        else
        {
            a.erase(a.find(l));
            b.erase(b.find(r));
        }

        if (a.size() && *a.rbegin() > *b.begin()) cout << "YES\n";
        else cout << "NO\n";
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t;
    // cin >> t;
    t = 1;
    while (t -- )
    {
        solve();
    }
}

E. Look Back

原题链接

题意

给出一个数组,每次操作可以将任意一个数乘2,问最少经过多少次操作,可以把数组变成非递减的数组

思路

先打表把2的次方存进数组

先骂自己,第一思路居然直接开始暴力模拟,让数组中的元素实时变化,不爆才怪啊、、、

实际上只需要记录下每个元素需要乘的2的数量,之后如果有后一个数比前一个数大的情况,可以看看大多少倍,用之前的2抵消

代码

#include 

using namespace std;

typedef pair<int, int> PII;

using i64 = unsigned long long;
typedef long long LL;

vector<i64> xsl(63);
vector<i64> xsl_re(63);

void solve()
{
    int n;
    cin >> n;
    vector<i64> a(n);
    for (int i = 0; i < n; i ++ ) cin >> a[i];

    i64 ans = 0;
    int t = 0;
    for (int i = 1; i < n; i ++ )
    {
        if (a[i] < a[i - 1])
        {
            double diff = 1.0 * a[i - 1] / a[i];
            int tmp = lower_bound(xsl.begin(), xsl.end(), diff) - xsl.begin();
            t += tmp;
            ans += t;
        }
        else
        {
            int diff = a[i] / a[i - 1];
            int tmp = lower_bound(xsl_re.begin(), xsl_re.end(), diff, greater<i64>()) - xsl_re.begin();
            tmp = 62 - tmp;
            t = max(0, t - tmp);
            ans += t;
        }
    }
    cout << ans << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    i64 res = 1;
    for (int i = 0; i < 63; i ++ )
    {
        xsl[i] = res;
        res *= 2;
    }
    xsl_re = xsl;
    reverse(xsl_re.begin(), xsl_re.end());

    int t;
    cin >> t;
    // t = 1;
    while (t -- )
    {
        solve();
    }
}

F. You Are So Beautiful

原题链接

题意

给出一个数组,问存在多少个子数组,使得数组中只能找到一个子序列和子数组相等

思路

这题很狡诈啊、、、专门来坑分不清子序列和子数组的笨比TAT

子数组一定是要连续的,但子序列不用连续!只要相对位置不改变就可以!!

所以这个子数组存在,当且仅当它的左端点第一次出现在数组中且右端点最后一次出现在数组中(真的很妙)

(另外在确定算法的时间复杂度没有问题的时候真的可以考虑一下是不是可以把unordered_map换成map,第二次遇到mapunordered_map快了

代码

#pragma GCC optimize(2)

#include 

using namespace std;

typedef pair<int, int> PII;

using i64 = long long;
typedef long long LL;

void solve()
{
    int n;
    cin >> n;
    vector<int> a(n);
    map<int, int> last;
    for (int i = 0; i < n; i ++ )
    {
        cin >> a[i];
        last[a[i]] = i;
    }

    i64 ans = 0, pre = 0;
    set<int> idx;
    for (int i = 0; i < n; i ++ )
    {
        if (!idx.count(a[i]))
        {
            pre ++ ;
            idx.insert(a[i]);
        }
        if (last[a[i]] == i) ans += pre;
    }

    cout << ans << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t;
    cin >> t;
    // t = 1;
    while (t -- )
    {
        solve();
    }
}

G1. Dances (Easy version)

原题链接

题意

给出两个数组,可以任意排序,每次操作可以同时在两个数组中分别删去一个元素,问至少多少操作才能让所有a[i] < b[i]

思路

先排序,然后双指针从前往后遍历,遇到不满足情况的就从b的开头和a的末尾删

代码

#include 

using namespace std;

typedef pair<int, int> PII;

const int mod = 998244353;

using i64 = long long;
typedef long long LL;

void solve()
{
    int n, m;
    cin >> n >> m;
    vector<int> a(n), b(n);
    a[0] = m;
    for (int i = 1; i < n; i ++ ) cin >> a[i];
    for (int i = 0; i < n; i ++ ) cin >> b[i];

    sort(a.begin(), a.end());
    sort(b.begin(), b.end());
    i64 cnt = 0;
    int ia = 0, ib = 0;
    for (ib = 0; ib < n; ib ++ )
    {
        if (a[ia] >= b[ib]) cnt ++ ;
        else ia ++ ;
    }

    cout << cnt << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t;
    cin >> t;
    // t = 1;
    while (t -- )
    {
        solve();
    }
}

G2. Dances (Hard Version)

原题链接

题意

和上一题一样,m换了范围

思路

不考虑是否满足情况,所有情况的个数n x m,原本就满足a < b的删去,可以和 m 组合使得符合条件的删去,剩下的就是不符合条件的了

代码

#include 

using namespace std;

typedef pair<int, int> PII;

const int mod = 998244353;

using i64 = long long;
typedef long long LL;

void solve()
{
    i64 n, m;
    cin >> n >> m;
    vector<int> a(n), b(n);
    for (int i = 1; i < n; i ++ ) cin >> a[i];
    for (int i = 0; i < n; i ++ ) cin >> b[i];

    sort(a.begin(), a.end());
    sort(b.begin(), b.end());

    multiset<i64> bb;
    i64 cnt = 0;
    for (int i = 0; i < n; i ++ ) bb.insert(b[i]);
    
    for (int i = 1; i < n; i ++ )
    {
        auto pos = bb.upper_bound(a[i]);
        if (pos != bb.end())
        {
            bb.erase(pos);
            cnt ++ ;
        }
    }

    i64 ans = cnt * m;
    i64 tmp = *bb.rbegin();
    ans += min(m, tmp - 1);
    cout << m * n - ans << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t;
    cin >> t;
    // t = 1;
    while (t -- )
    {
        solve();
    }
}

你可能感兴趣的:(Codeforces,题解,算法)