码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-01-31 18:44:55      阅读:88      评论:0      收藏:0      [点我收藏+]

标签:include   bool   puts   void   主席树   val   长度   解题报告   upd   

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\n", 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\n", ans[i]);
    
        return 0;
    }
    

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

题意:
  • 给定\(n\)道题,每道题有\(k\)个选项,有且仅有一个正确答案。
  • 现在给你犯傻了,把第\(1\)题涂到了第\(2\)题,第\(2\)题涂到了第\(3\)\(,...,\)\(n\)题涂到了第\(1\)题。
  • 给你原先\(n\)道题的答案,问你有多少种答案的可能,让你错误提交之后比你正确提交的分数高。
数据范围:
  • \(Easy Version\ 1\leq n\leq 2000\)
  • \(Hard Version\ 1\leq n\leq 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)无)

标签:include   bool   puts   void   主席树   val   长度   解题报告   upd   

原文地址:https://www.cnblogs.com/zxytxdy/p/12246009.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!