Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) 解题报告(A~F)(E/F(hard)无)(示例

Posted zxytxdy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) 解题报告(A~F)(E/F(hard)无)(示例相关的知识,希望对你有一定的参考价值。

Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) 解题报告(A~F)(E/F(hard)无)

A:Math Problem

  • 目标区间右端点一定是所有给定区间左端点的最大值。

  • 目标区间左端点一定是所有给定区间右端点的最小值。

  • 输出即可,注意负数。

  • #include<bits/stdc++.h>
    using namespace std;
    int T, n, a, b;
    int main()
    {
        cin >> T;
        while(T--)
        {
            int a = 0, b = 1e9;
            scanf("%d", &n);
            for(int i = 1, x, y; i <= n; i++)
            {
                scanf("%d%d", &x, &y);
                a = max(a, x); b = min(b, y);
            }
            cout << max(0, a - b) << endl;
        }
        return 0;
    }

B:Box?

  • 贪心。

  • 假设输入序列为(a)序列,答案序列为(b)序列。

  • 那么如果(a)序列上升了,(b)序列自然就是(a)序列的值。

  • 如果(a)序列不变,那么就从没有出现的数字中取最小的数字。

  • 贪心过程中注意特判无解情况。

  • #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 10;
    int T, n;
    int a[maxn];
    int b[maxn];
    
    void solve()
    {
        scanf("%d", &n); set<int> s;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            s.insert(i);
        }
    
        int mx = 0;
        for(int i = 1; i <= n; i++)
        {
            if(a[i] > mx)
            {
                if(s.count(a[i]))
                {
                    b[i] = a[i];
                    s.erase(b[i]);
                    mx = a[i];
                } else{
                    puts("-1");
                    return;
                }
            }
            else if(a[i] == mx)
            {
                if(*s.begin() > mx)
                {
                    puts("-1");
                    return;
                }
                b[i] = *s.begin();
                s.erase(s.begin());
            }
        }
        for(int i = 1; i <= n; i++)
            printf("%d ", b[i]); puts("");
    }
    
    int main()
    {
        scanf("%d", &T);
        while(T--) solve();
        return 0;
    }

C:Messy?

  • 首先这题初看很难做,但其实分析一下就可以转换为一个构造题。

  • 首先长度为(n)的序列经过(n)次的操作一定能变成任意的括号序列。

  • 那么其实我们只需要把序列构造成满足有(k)个合法前缀的情况。

  • 怎么构造呢,先加入(k-1)(()),然后剩余的全部都加上(((..)))这样的。

  • 打个比方:

    • 比如说样例(1)(8 2),就先构造(()),然后加上(((())))
    • 样例(2)(10 3),就构造(()()),然后加上(((())))
  • 之后根据最终的序列构造操作。

  • #include<bits/stdc++.h>
    #define PII pair<int, int>
    using namespace std;
    const int maxn = 2000 + 10;
    int T, n, k;
    string s, ans;
    
    void swap_str(int x, int y)
    {
        if(x > y) swap(x, y);
        while(x < y)
        {
            swap(s[x], s[y]);
            x++, y--;
        }
    }
    
    void get_op()
    {
        vector<PII> op;
        for(int i = 0; i < (int)s.size(); i++)
        {
            if(s[i] != ans[i])
            {
                for(int j = i+1; j < (int)s.size(); j++)
                {
                    if(s[i] != s[j])
                    {
                        swap_str(i, j);
                        op.push_back({i+1, j+1});
                        break;
                    }
                }
            }
        }
    
        cout << op.size() << endl;
        for(auto x : op)
            printf("%d %d
    ", x.first, x.second);
    }
    
    int main()
    {
        scanf("%d", &T);
        while(T--)
        {
            scanf("%d%d", &n, &k);
            cin >> s; ans = "";
            for(int i = 1; i <= k - 1; i++)
                ans += '(', ans += ')';
    
            int tmp = n - (k-1) * 2;
            for(int i = 1; i <= tmp / 2; i++) ans += '(';
            for(int i = 1; i <= tmp / 2; i++) ans += ')';
            //cout << ans << endl;
            get_op();
        }
        return 0;
    }
    

D:Optimal Subsquences(Hard Version)

  • 主席树+贪心。

  • 我们需要满足子序列和最大,所以这(k)个数,一定是我们将序列从大到小排序后的前(k)个数字。

  • 于是乎我要选择的(k)个数确定了。

  • 同时我们需要保证字典序最小。

  • (num)为第(k)大的元素,要想让字典序最小,那么我们需要做的就是让所有(num)出现的位置尽可能靠前,也就是尽可能的挑选(idx)小的元素。

  • 所以我们记录序列(val)时同时记录序列的(id),之后按照(val)排序,(val)相等的两项让(id)小的在前。

  • 之后用主席树维护(id)

  • 对于每次询问,找到对应的树与空树进行查询第(pos)大的数在原序列的位置,找到这个数即为答案。

  • #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 2e5 + 10;
    int n, m, mp[maxn];  //mp(i)表示第i次询问对应的那个版本的线段树
    
    struct Arr
    {
        int v, id;
    }a[maxn];
    int b[maxn];
    
    bool cmp(Arr a, Arr b)
    {
        if(a.v == b.v) return a.id < b.id;
        return a.v > b.v;
    }
    
    struct Node{
        int k, pos, id;
    }q[maxn];
    
    
    bool cmp1(Node a, Node b){
        return a.k < b.k;
    }
    
    int ans[maxn];
    
    //-----------主席树部分
    int sum[maxn<<5], ls[maxn<<5], rs[maxn<<5];
    int rt[maxn<<5], tot;
    
    int build(int l, int r)
    {
        int root = ++tot;
        if(l == r) return root;
        int mid = (l + r) >> 1;
        ls[root] = build(l, mid);
        rs[root] = build(mid+1, r);
        return root;
    }
    
    int update(int pre, int l, int r, int k)
    {
        int root = ++tot;
        ls[root] = ls[pre], rs[root] = rs[pre], sum[root] = sum[pre] + 1;
        if(l == r) return root;
        int mid = (l + r) >> 1;
        if(k <= mid) ls[root] = update(ls[pre], l, mid, k);
        else rs[root] = update(rs[pre], mid+1, r, k);
        return root;
    }
    
    int query(int u, int v, int l, int r, int k)
    {
        if(l == r) return l;
        int x = sum[ls[v]] - sum[ls[u]];
        int mid = (l + r) >> 1;
        if(k <= x) return query(ls[u], ls[v], l, mid, k);
        else return query(rs[u], rs[v], mid+1, r, k - x);
    }
    
    //-------------------
    
    void init()
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i].v);
            a[i].id = i;
            b[i] = a[i].v;
        } sort(a+1, a+1+n, cmp);
    
    
        scanf("%d", &m);
        for(int i = 1, x, y; i <= m; i++){
            scanf("%d%d", &x, &y);
            q[i] = {x, y, i};
        } sort(q+1, q+1+m, cmp1);
    }
    
    int main()
    {
        init();
        rt[0] = build(1, n);
        int p = 1;
        for(int i = 1; i <= m; i++)
        {
            if(q[i].k < p)
            {
                mp[i] = mp[i-1];  //k相等的话用的是同一棵线段树
                continue;
            }
            //插入对应的id
            for(int j = p; j <= q[i].k; j++)
                rt[j] = update(rt[j-1], 1, n, a[j].id); 
            mp[i] = rt[q[i].k];
            p = q[i].k + 1;
        }
        for(int i = 1; i <= m; i++)
        {
            int t = query(rt[0], mp[i], 1, n, q[i].pos);
            ans[q[i].id] = b[t];
        }
    
        for(int i = 1; i <= m; i++)
            printf("%d
    ", ans[i]);
    
        return 0;
    }
    

F:Wrong Answer on test 233(Easy/Hard Version)

题意:
  • 给定(n)道题,每道题有(k)个选项,有且仅有一个正确答案。
  • 现在给你犯傻了,把第(1)题涂到了第(2)题,第(2)题涂到了第(3)(,...,)(n)题涂到了第(1)题。
  • 给你原先(n)道题的答案,问你有多少种答案的可能,让你错误提交之后比你正确提交的分数高。
数据范围:
  • (Easy Version 1leq nleq 2000)
  • (Hard Version 1leq nleq 10^5)
思路((Easy Version)):
  • (dp)

  • (f(i,j))表示考虑到了第(i)题,交换后比交换前多得(j)分的结果数。

  • 如果(h(i)==h(i+1))的话,(f(i,j)=f(i-1,j)),因为这时候交换并不会增加结果。

  • 其他情况,(f(i,j)=f(i-1,j+1)+f(i-1,j-1)+(k-2)*f(i-1,j))

  • #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 2e3 + 10;
    const int mod = 998244353;
    ll n, k, h[maxn], f[maxn][maxn<<1];
    
    int main()
    {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> k;
        for(int i = 1; i <= n; i++)
            cin >> h[i];
    
        if(k == 1)
        {
            puts("0");
            return 0;
        }
    
        f[0][2001] = 1;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j < maxn<<1; j++)
        {
            if(h[i] == h[i%n+1]) f[i][j] = (f[i-1][j]*k)%mod;
            else f[i][j] = (f[i-1][j-1]+f[i-1][j+1]+f[i-1][j]*(k-2))%mod;
        } ll ans = 0;
        for(int i = 1; i <= n; i++)
            ans = (ans + f[n][2001+i]) % mod;
        cout << ans << endl;
    
        return 0;
    }

以上是关于Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) 解题报告(A~F)(E/F(hard)无)(示例的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) B. Box 贪心

Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) B Box

Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3)

Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) C. Messy 构造

Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) C Messy

Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3