Codeforces Round #843 (Div. 2)

Codeforces Round #843 (Div. 2)

https://codeforces.com/contest/1775
CD都不会写的垃圾罢了

A1. Gardener and the Capybaras (easy version)

暴力枚举分界点

#include <bits/stdc++.h>

using namespace std;

void solve () {
    string s;
    cin >> s;
    for (int i = 0; i < s.size (); i++) {
        for (int j = i + 1; j < s.size (); j++) {
            //0,i  i+1,j   j+1,n
            string a = s.substr (0, i + 1), b = s.substr (i + 1, j - i), c = s.substr (j + 1, s.size () - j);
            if (max({a, b, c}) == b || min ({a, b, c}) == b) {
                cout << a << ' ' << b << ' ' << c << endl;
                return ;
            }
        }
    }

    cout << ":(" << endl;
}

int main () {
    int t;
    cin >> t;
    while (t --)    solve ();
}

//aab
//baa
//baaaa

A2. Gardener and the Capybaras (hard version)

分别构造 \(min,max\),考虑贪心:

构造 \(max\) 时,要保证两端的字符串尽可能小,则左边取一个一定是最小的,即 \(s_0\)
考虑右边如何最小:

  1. \(s在[2,n)\) 范围内只有一种元素,即只存在 \('a'\)\('b'\),则取最后一个一定是最小,即 \(s_{n-1}\)
  2. 否则的话,以 \('a'\) 开头的后缀一定是最小的,则只需找到最后一个 \('a'\) 所在位置 \(pos\),右边的串就取 \([pos,n)\)
    这样剩下的就是中间串了,然后比较一下中间是否最大就行了。

构造 \(min\) 时,如果在 \([1,n-1)\) 范围内能找到一个 \('a'\) 的话一定能构造(这已经是最小字符串了)。
否则的话就是只有类似下面这四种情况:

不难发现这些情况都能够造出中间是 \(max\) 的分配方案。

所以一定有解。

#include <bits/stdc++.h>

using namespace std;

void solve () {
    string s, a, b, c;
    cin >> s;
    int n = s.size ();
    //max
    a = s[0];
    int find = -1, cntb = 0;
    for (int i = 2; i < n; i++) {
        if (s[i] == 'b')    cntb ++;
    }
    for (int i = n - 1; i > 1; i--) {
        if (s[i] == 'a') {
            find = i;
            break;
        }
    }
    if (find == -1 || !cntb) {
        b = s.substr (1, n - 2);
        c = s[n - 1];
    }
    else {
        int len1 = find - 1, len2 = n - find;
        b = s.substr (1, len1), c = s.substr (find, len2);
    }
    if (b == max ({a, b, c})) {
        cout << a << ' ' << b << ' ' << c << endl;
        return ;
    }

    //min
    int posa = -1;
    for (int i = 1; i < n - 1; i++) {
        if (s[i] == 'a') {
            posa = i;
            break;
        }
    }
    if (posa != -1) {
        for (int i = 0; i < posa; i++)  cout << s[i];
        cout << ' ' << s[posa] << ' ';
        for (int i = posa + 1; i < n; i++)  cout << s[i];
        cout << endl;
        return ;
    }

    cout << ":(" << endl;
}

int main () {
    int t;
    cin >> t;
    while (t --)    solve ();
}

//aab
//baa
//baaaa

B. Gardener and the Array

若不存在唯一元素(即少了他就不是全集)的元素就能构造出。
证明见:https://zhuanlan.zhihu.com/p/598160930

#include <bits/stdc++.h>

using namespace std;

void solve () {
    int n, m, x;
    cin >> n;
    map<int, int> mp;
    vector <int> v[n+1];
    for (int i = 1; i <= n; i++) {
        cin >> m;
        for (int j = 1; j <= m; j++) {
            cin >> x;
            mp[x] ++;
            v[i].push_back (x);
        }
    }
    for (int i = 1; i <= n; i++) {
        bool suc = true;
        for (auto j : v[i]) {
            mp[j] --;
            if (mp[j] == 0) {
                suc = false;
                break;
            }
            mp[j] ++;
        }
        if (suc) {
            cout << "Yes\n";
            return ;
        }
    }  
    cout << "No\n";
}

int main () {
    int t;
    cin >> t;
    while (t --)    solve ();
}


//check相邻的两个数是否有包含关系

C. Interesting Sequence

位运算小结论:https://zhuanlan.zhihu.com/p/598221402
注意左移的时候1要取 \(ll\)

#include <bits/stdc++.h>
#define int long long

using namespace std;

void solve () {
    int n, m;
    cin >> n >> m;
    int r = 5e18 + 1, l = n; //至少为n!!
    for (int i = 0; i < 60; i++) {
        int a = n >> i & 1, b = m >> i & 1;
        if (!a && b) {
            cout << "-1\n";
            return ;
        }
        if (!a && !b)     continue;
        int k = (n / (1ll << i) + 1) * (1ll << i); //>=n的最小的一个该位为0的数
        //cout << k << endl;
        if (a && !b)     l = max (l, k);
        else    r = min (r, k - 1); //逐步缩小范围
    }
    if (r < l)      cout << "-1\n";
    else    cout << l << endl;
}

signed main () {
    int t;
    cin >> t;
    while (t --)    solve ();
    //cout << log2 (1e18);
}

//n进行二进制分解
//n上是0而x上是1, 则不可能
//m>=n
//二进制小结论: >=n的最小的一个该位为0的数: n / (1 << i) + 1

D. Friendly Spiders

图论 + 数论
挺好的一道题,学到了一个建图小技巧。
本来有 \(n\) 个点互相可达的话要建 \(n*(n-1)/2\) 条边,而如果使用下图这种建图方法的话就会将边数变为 \(2n\),把空间从 \(O(n^2)\) 优化成 \(O(n)\)

我们把 \(p\) 叫做虚点,对于任意一个数素因数分解之后与该因子连边即可。(为什么只看素因数? 因为包含相同素因子的点会重复连边,在造成空间冗余,而不同素因子之间一定是不相互包含的,故只需考虑素因子)
然后边权只有 \(0,1\) 两种情况,故可以使用 \(01bfs\) 求最短路。
\(01bfs\) 例题

#include <bits/stdc++.h>

using namespace std;
typedef pair<int, int> pii;
const int N = 3e5 + 5, M = 2 * N;
int n, s, t, dis[M], lst[M];
vector <pii> vi[M];

int main () {
    cin >> n;
    //建图技巧
    for (int i = 1; i <= n; i++) { //以素因子j为虚点, 就只需要建立O(n)级别的边了
        int u;  cin >> u;
        for (int j = 2; j * j <= u; j++) {
            if (u % j == 0) {
                while (u % j == 0)  u /= j;
                vi[i].push_back ({N + j, 0});
                vi[N + j].push_back ({i, 1});
            }
        }
        if (u > 1) {
            vi[i].push_back ({N + u, 0});
            vi[N + u].push_back ({i, 1});
        }
    }
    cin >> s >> t;

    //0-1bfs求最短路
    deque<int> q; //0放front, 1放back
    q.push_back (s);
    dis[s] = 1;
    while (!q.empty ()) {
        auto u = q.front ();
        q.pop_front ();
        for (auto i : vi[u]) {
            int v = i.first, dist = i.second;
            if (dis[v])     continue;
            if (dist == 1)  q.push_back (v); //1尾
            else    q.push_front (v); //0首
            dis[v] = dis[u] + dist;
            lst[v] = u; //记录前驱, 输出路径
        }
    }

    //输出路径
    if (dis[t] == 0)    cout << -1;
    else {
        cout << dis[t] << endl;
        stack<int> stk;
        int ed = t;
        while (ed != s) {
            stk.push (ed);
            ed = lst[ed];
        }
        stk.push (s); //别忘了把起点放进去
        while (!stk.empty ()) {
            auto ans = stk.top ();
            if (ans < N)    cout << ans << ' ';
            stk.pop ();
        }
    }

}

E. The Human Equation

sb结论题。
https://zhuanlan.zhihu.com/p/598179357

#include <bits/stdc++.h>
#define int long long

using namespace std;
int n, x, minn, maxn, sum;

void solve () {
    int n, x;
    cin >> n;
    minn = maxn = sum = 0;
    while (n --) {
        cin >> x;
        sum += x;
        //cout << sum << ' ';
        minn = min (minn, sum), maxn = max (maxn, sum);
    }
    cout << maxn - minn << endl;
}

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

//转化为区间和, 与0取max,min

F. Laboratory on Pluto

posted @ 2023-01-10 23:01  Sakana~  阅读(203)  评论(1编辑  收藏  举报