Loading

CodeCraft-22 and Codeforces Round #795 (Div. 2) A - E

传送门

感觉这一场的题目真的非常棒,找时间一定要把 E 也给补了(除非看完答案还是不会),虽然现在还没看,但是前几题的质量让我有继续做下去的欲望

更新:E 题已经补了,感觉还挺不错

A. Beat The Odds

要么全是奇数,要么全是偶数,看着哪个少就删哪个

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e17 + 10;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        int l = 0, r = 0;
        int n;
        cin >> n;
        for(int i=0; i<n; i++)
        {
            int x;
            cin >> x;
            if(x & 1) l++;
            else r++;
        }
        cout << min(l, r) << endl;
    }

    return 0;
}

B. Shoe Shuffling

从最大的开始想,显然只能和自己一样的换,因此可以推出只能和自己一样的换

先找出 -1 的情况:有一种鞋子出现的频率为 1

接着构造就好了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e17 + 10;
int num[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        int n;
        cin >> n;
        int ans = n;
        for(int i=1; i<=n; i++) cin >> num[i];
        num[0] = num[1];
        num[n + 1] = num[n] + 1;
        int pre = 0;
        for(int i=1; i<= n + 1; i++)
        {
            if(num[i] == num[i-1]) pre++;
            else
            {
                ans = min(ans, pre);
                pre = 1;
            }
        }
        if(ans <= 1) cout << -1 << endl;
        else
        {
            int cur = 1;
            for(int i=1; i<=n; i++)
            {
                if(i != 1) cout << " ";
                if(num[i+1] != num[i])
                {
                    cout << cur;
                    cur = i + 1;
                }
                else cout << i + 1;
            }
            cout << endl;
        }
    }

    return 0;
}

C - Sum of Substrings

优先级:

  1. 最后一个位置 —— 只会产生一个个位数

  2. 第一个位置 —— 只会产生一个十位数

  3. 其他位置 —— 生成一个个位数、一个十位数

因此就考虑把最靠近末尾的 1 先挪到末尾,再把最靠近开头的 1 挪到开头

注意不要把已经挪到末尾的 1 挪到最开头去

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e17 + 10;
int num[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        int n, m;
        cin >> n >> m;
        string s;
        cin >> s;
        if(s[n-1] == '0')
        {
            int f = n - 1;
            for(int i=n-1; i>=0; i--)
            {
                if(s[i] == '1')
                {
                    f = i;
                    break;
                }
            }
            if(n - f - 1 <= m)
            {
                m -= n - f - 1;
                swap(s[n-1], s[f]);
            }
        }
        if(s[0] == '0' && m)
        {
            int f = 0;
            for(int i=0; i<n; i++)
            {
                if(s[i] == '1')
                {
                    f = i;
                    break;
                }
            }
            if(f <= m && f != n -1)
            {
                swap(s[0], s[f]);
            }
        }
        int ans = 0;
        int f = 0;
        for(int i=1; i<n-1; i++)
            f += s[i] - '0';
        ans += (s[0] - '0') * 10 + (s[n-1] - '0') + f * 11;
        cout << ans << endl;
    }

    return 0;
}

D - Max GEQ Sum

单调栈 + 线段树

这题非常值得

思路:

假设我们此时的最大值为 \(a_i\),那么我们考虑不等式是否成立时,就应该考虑一个最大的区间 \([l,r]\),在这里找有没有不符合不等式的情况出现

最大区间 \([l, r]\)\(a_{l - 1} > a_i\)\(a_{r + 1} > a_i\)

如何找到不符合不等式的情况:

\(sum(l,r) > a_i\)

-> \(sum(l, i-1) + sum(i+1, r) > 0\)

因为我们只要在 \([l, r]\) 上找到一个任意的包含 \(a_i\) 的区间,使得其和大于 \(a_i\) 即可,因此我们只需要考虑其两边任意一个区间大于 0 即可

-> \(sum(l,i-1) > 0\)\(sum(i+1,r) > 0\)

实现:

  1. 找到区间 \([l, r]\),即找到每个 \(a_i\) 左右两边第一个大于 \(a_i\) 的数

这里考虑使用单调栈,栈底到栈顶,从大到小维护,碰到比当前数字小的就弹出

  1. 找不等式不成立的情况

这里我们改成 \(sum(l,i) > a_i\)\(sum(i,r) > a_i\) 来实现

我们考虑区间和的时候使用前缀和 \(S\) 维护,以 \(sum(l,i) > a_i\) 为例:

\(sum(j,i) = S_i - S_{j-1}\) \((l \le j \le i)\)

不难发现,在查找的区间中,\(S_i\) 是不变的,因此我们可以转化成为在 \([l-1, i-1]\) 中,找一个 \(S\) 的最小值

这个查找可以考虑用线段树来实现,同理对于右边的情况可以用查找最大值来实现

单调栈:\(O(n)\)

总体线段树查询:\(O(nlogn)\)

时间复杂度:\(O(nlogn)\)

挺可惜没想到线段树 + 前缀和维护,想半天一直卡在找的上面

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e17 + 10;
ll num[maxn], sum[maxn];
int pre_l[maxn], pre_r[maxn];

void st_solve(int n)
{
    stack<int>st;
    st.push(0);
    num[0] = num[n+1] = inf;
    for(int i=1; i<=n; i++)
    {
        while(num[st.top()] <= num[i]) st.pop();
        pre_l[i] = st.top();
        st.push(i);
    }
    while(st.size()) st.pop();
    st.push(n+1);
    for(int i=n; i>=1; i--)
    {
        while(num[st.top()] <= num[i]) st.pop();
        pre_r[i] = st.top();
        st.push(i);
    }
    num[0] = num[n+1] = 0;
}

struct node
{
    ll maxx, minn;
}tr[maxn << 2];

void push_up(int now)
{
    tr[now].maxx = max(tr[now << 1].maxx, tr[now << 1 | 1].maxx);
    tr[now].minn = min(tr[now << 1].minn, tr[now << 1 | 1].minn);
}

void build(int now, int l, int r)
{
    if(l == r)
    {
        tr[now].maxx = tr[now].minn = sum[l];
        return;
    }
    int mid = l + r >> 1;
    build(now << 1, l, mid);
    build(now << 1 | 1, mid + 1, r);
    push_up(now);
}

ll query_min(int now, int l, int r, int L, int R)
{
    if(L <= l && r <= R)
        return tr[now].minn;
    int mid = l + r >> 1;
    ll ans = inf;
    if(L <= mid) ans = query_min(now << 1, l, mid, L, R);
    if(R > mid) ans = min(ans, query_min(now << 1 | 1, mid + 1, r, L, R));
    return ans;
}

ll query_max(int now, int l, int r, int L, int R)
{
    if(L <= l && r <= R)
        return tr[now].maxx;
    int mid = l + r >> 1;
    ll ans = -inf;
    if(L <= mid) ans = query_max(now << 1, l, mid, L, R);
    if(R > mid) ans = max(ans, query_max(now << 1 | 1, mid + 1, r, L, R));
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        int n;
        cin >> n;
        for(int i=1; i<=n; i++)
        {
            cin >> num[i];
            sum[i] = sum[i-1] + num[i];
        }
        st_solve(n);
        build(1, 0, n);
        int f = 1;
        for(int i=1; i<=n && f; i++)
        {
            int l = pre_l[i];
            int r = pre_r[i];
            ll l_val = query_min(1, 0, n, l, i - 1);
            ll r_val = query_max(1, 0, n, i, r - 1);
            if(max(sum[i] - l_val, r_val - sum[i-1]) > num[i]) f = 0;
        }
        if(f) cout << "YES" << endl;
        else cout << "NO" << endl;
    }

    return 0;
}

E. Number of Groups

并查集 + set

我觉得这个题 cf 自己的题解写的挺好的

把所有的区间段化成左端点和右端点的方式,然后从左到右逐个遍历点,这样就可以降维

如果当前访问到一个左端点,则加入到 set 当中,表示现在是处于这个区间段上的,当然当前的点可以处于多个区间段内

如果当前访问到一个右端点,就直接删除掉 set 中,这个段的左端点,表示现在已经不在这个区间段内了

在访问左端点的时候,当前线段显然于存在 set 中的所有线段有交点,这样就可以直接开始用并查集维护了

如果都是重合到一起,显然操作还是 \(O(n^2)\)

其实我们只需要留下一个能合并的区间中,右端点最远的作为这个集合的代表,提供给后面的线段进行合并,其他的都是可以删掉的,这样建图就能保证是在 \(O(n)\) 的了

然后有两种颜色,所以要用两个 set 去分别维护两种颜色,操作都和上述的表示一致

复杂度为 \(O(nlogn)\)

#include <iostream>
#include <cstdio>
#include <set>
#include <map>
#include <vector>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define pii pair<int, int>
const int maxn = 1e5 + 10;
int top[maxn], vis[maxn << 1];

struct node
{
    int t, l, r, id;
}seg[maxn];

int query(int x)
{
    return x == top[x] ? x : top[x] = query(top[x]);
}

inline void comb(int x, int y)
{
    int fx = query(x);
    int fy = query(y);
    if(fx != fy)
        top[fx] = fy;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        int n;
        cin >> n;
        map<int, vector<pii>>mp;
        for(int i=0; i<=n; i++) top[i] = i;
        for(int i=1; i<=n; i++)
        {
            int t, l, r;
            cin >> t >> l >> r;
            seg[i] = {t, l, r, i};
            mp[l].push_back({0, i});
            mp[r].push_back({1, i});
        }
        set<int>s[2];
        for(auto it=mp.begin(); it!=mp.end(); it++)
        {
            sort(it->second.begin(), it->second.end());
            for(int i=0; i<it->second.size(); i++)
            {
                pii now = it->second[i];
                node p = seg[now.second];
                if(now.first == 0)
                {
                    if(s[p.t ^ 1].size())
                    {
                        int way = -1, nex = 0;
                        for(auto j=s[p.t ^ 1].begin(); j!=s[p.t ^ 1].end(); j++)
                        {
                            comb(*j, now.second);
                            vis[*j] = 0;
                            if(seg[*j].r > way)
                            {
                                way = seg[*j].r;
                                nex = *j;
                            }
                        }
                        s[p.t ^ 1].clear();
                        s[p.t ^ 1].insert(nex);
                        vis[nex] = 1;
                    }
                    s[p.t].insert(p.id);
                    vis[p.id] = 1;
                }
                else
                {
                    if(vis[now.second])
                    {
                        vis[now.second] = 0;
                        s[p.t].erase(p.id);
                    }
                }
            }
        }
        int ans = 0;
        for(int i=1; i<=n; i++) ans += top[i] == i;
        cout << ans << endl;
    }
    return 0;
}
posted @ 2022-06-04 22:58  dgsvygd  阅读(26)  评论(0编辑  收藏  举报