zoukankan      html  css  js  c++  java
  • 2019 年百度之星·程序设计大赛

    传送门

    A.度度熊与数字

    签到。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int T;
    int v;
    int calc(int x) {
        int res = 0;
        while(x) {
            res += x % 10;
            x /= 10;
        }
        return res;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> v;
            vector <int> ans;
            int x = calc(v);
            for(int i = 1; i <= x; i++) {
                if(x % i == 0 && v % i == 0) ans.push_back(i);
            }
            cout << ans.size() << '
    ';
            for(int i = 0; i < ans.size(); i++) cout << ans[i] << " 
    "[i == ans.size() - 1];
        }
        return 0;
    }
    

    B.度度熊与排列

    题意:
    有一个序列(p)(p_i)表示将第(i)个位置的数移到第(p_i)位。
    现在序列(p)丢失了,但输入串和输出串还在。
    问是否能找到一个字典序最小的(p)

    思路:

    • 对于第一对串直接暴力处理,利用一个vector来存储所有可能的位置。
    • 之后暴力枚举后面所有的串,然后不断删去一些位置。
    • 最后输出即可。

    注意不合法条件的判断:

    • 输入和输出串相关字符个数不相等。
    • 某个字符串没有合法位置。

    反正我这么来写细节有点多。
    感觉题解思路挺好的,可以注意到如果把每个输入串的第(i)个连接起来,如果存在合法情况,那么一定存在某个(j),把每个输出串的第(j)个连接起来,两个串相等。
    然后直接暴力枚举即可。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 55;
    int T;
    int n, m;
    vector <int> p[N];
    char s[N], t[N];
    int cnt1[26], cnt2[26];
    bool check(char *s, char *t) {
        memset(cnt1, 0, sizeof(cnt1)); memset(cnt2, 0, sizeof(cnt2));
        for(int i = 1; i <= m; i++) cnt1[s[i] - 'a']++;
        for(int i = 1; i <= m; i++) cnt2[t[i] - 'a']++;
        bool ok = 1;
        for(int i = 0; i < 26; i++) if(cnt1[i] != cnt2[i]) ok = 0;
        return ok;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            int ok = 1;
            cin >> n >> m;
            for(int i = 1; i <= m; i++) p[i].clear();
            cin >> s + 1 >> t + 1;
            if(!check(s, t)) ok = 0;
            for(int i = 1; i <= m; i++) {
                for(int j = 1; j <= m; j++) {
                    if(t[j] == s[i]) p[i].push_back(j);
                }
            }
            for(int i = 1; i < n; i++) {
                cin >> s + 1 >> t + 1;
                if(!check(s, t)) ok = 0;
                for(int j = 1; j <= m; j++) {
                    for(auto it = p[j].begin(); it != p[j].end(); ++it) {
                        if(t[*it] != s[j]) {
                            p[j].erase(it); --it;
                        }
                    }
                }
            }
            set <int> S;
            for(int i = 1; i <= m; i++) {
                for(auto it : p[i]) S.insert(it);
                if((int)p[i].size() == 0) ok = 0;
            }
            if((int)S.size() != m) ok = 0;
    //        for(int i = 1; i <= m; i++) {
    //            for(auto it : p[i]) {
    //                cout << it << ' ';
    //            }
    //            cout << '
    ';
    //        }
            if(ok == 0) {
                cout << -1 << '
    ';
            } else {
                S.clear();
                for(int i = 1; i <= m; i++) {
                    for(auto it : p[i]) {
                        if(S.count(it) == 0) {
                            S.insert(it);
                            cout << it << " 
    "[i == m];
                            break;
                        }
                    }
                }
            }
        }
        return 0;
    }
    

    C.度度熊与运算式 1

    题意:
    有如下运算式:

    [1 op_1 1 op_2 1cdots1 op_n 1 ]

    每个(op)有两种可能:^或者(?)(?)表示可以为(+)或者^。
    给出(op_i),确定表达式最大的值为多少。

    思路:
    以^为分隔符,运算式则可以分割为连续的几段。
    之后有几个重要的观察:

    • 最终答案的奇偶性和(n+1)的奇偶性相同;
    • 每一段都可以对其进行二进制划分;
    • 如果最终存在多个段等于(2^k),可以知道对于(1leq ileq k),都存在(2^i)的二进制划分。那么把(2^k)全部划分为(1)答案不可能变差的。

    然后就没了。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    int T;
    char s[1 << 21];
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        vector <int> v;
        while(T--) {
            v.clear();
            cin >> s + 1;
            int n = strlen(s + 1);
            int cnt = 1;
            for(int i = 1; i <= n; i++) {
                if(s[i] == '^') {
                    v.push_back(cnt);
                    cnt = 1;
                } else cnt++;
            }
            v.push_back(cnt);
            sort(v.begin(), v.end());
            int ans = 0;
            for(int i = 20; i; i--) {
                for(auto &it : v) {
                    if(it >= (1 << i)) {
                        it -= (1 << i);
                        ans ^= (1 << i);
                        break;
                    }
                }
            }
            ans ^= ((n + 1) & 1);
            cout << ans << '
    ';
        }
    
        return 0;
    }
    

    D.度度熊与组题

    题意:
    现有(2n)道题,每道题有一个难度(a_i)
    现在要将这些题分配给两套题,最终套题的难度等于(sum|a_{i1}-a_{i2}|)
    现问使得最终套题难度最小的方案数有多少。

    思路:
    有一个观察:

    • 难度最小方案中,难度小于等于(v)的题的数量与难度小于等于(v-1)的题的数量相差的绝对值不会超过(1)

    其实画图理解起来更加直观一点,但我把图画出来为什么没有意识到这点= =还是太菜了。
    那么可以以(v)为阶段来进行(dp),因为阶段之间的关系已经明确了。
    之后分奇偶情况讨论即可(满足情况在限制条件之中)。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5, MOD = 1e9 + 7;
    int T, n;
    int a[N], b[N];
    void Hash() {
        sort(b + 1, b + n + 1);
        b[0] = unique(b + 1, b + n + 1) - b - 1;
        for(int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + b[0] + 1, a[i]) - b;
    }
    ll dp[N], fac[N], inv[N];
    int cnt[N];
    ll qp(ll a, ll b) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % MOD;
            a = a * a % MOD;
            b >>= 1;
        }
        return ans;
    }
    void pre() {
        fac[0] = 1;
        for(int i = 1; i < N; i++) fac[i] = fac[i - 1] * i % MOD;
        inv[N - 1] = qp(fac[N - 1], MOD - 2);
        for(int i = N - 2; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % MOD;
    }
    ll C(int n, int m) {
        if(n < m || n < 0 || m < 0) return 0;
        return fac[n] * inv[m] % MOD * inv[n - m] % MOD;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        pre();
        cin >> T;
        while(T--) {
            cin >> n; n <<= 1;
            for(int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i];
            Hash();
            for(int i = 1; i <= b[0]; i++) cnt[i] = 0;
            dp[0] = 1;
            sort(a + 1, a + n + 1);
            for(int i = 1; i <= n; i++) cnt[a[i]]++;
            int tot = 0;
            for(int i = 1; i <= b[0]; i++) {
                if(tot & 1) {
                    if(cnt[i] & 1) {
                        dp[i] = dp[i - 1] * C(cnt[i], cnt[i] >> 1) % MOD;
                    } else {
                        dp[i] = dp[i - 1] * (C(cnt[i], cnt[i] >> 1) + C(cnt[i], cnt[i] / 2 - 1)) % MOD;
                    }
                } else {
                    if(cnt[i] & 1) {
                        dp[i] = dp[i - 1] * C(cnt[i], cnt[i] >> 1) % MOD * 2ll % MOD;
                    } else {
                        dp[i] = dp[i - 1] * C(cnt[i], cnt[i] >> 1) % MOD;
                    }
                }
                tot += cnt[i];
            }
            cout << dp[b[0]] << '
    ';
        }
        return 0;
    }
    

    E.度度熊与整数集合

    题意:
    有一个正整数集合(S),一开始为(1)~(N)

    • 每次选择一个集合,对于集合中的(i,cdots,j),把(a_i,cdots,a_j)都加上(1)
    • 选择一个(x),以(x)为分界将集合划分为两个集合,(x)包含在左边集合。
    • 原集合消失。

    不断重复上面过程,最终会得到序列(a)
    现在有最终的序列(a),求出字典序最小的(x)序列,或者可能不存在合法的(x)序列。

    思路:
    这是一个很有意思的题。
    一开始想感觉很多东西都想不清楚,但只要思路正确一下就比较明朗了。
    只要注意到每次操作就相当于在树上多出两个子节点,并且每个结点的深度即为目前的(a)值。
    那么给出的(a_i)就是最终的叶子结点,每次的询问就相当于在这颗树上先往左边走得到的答案。

    接下来考虑把树构建出来就行了。
    构树的话就是不断将结点合并,每次合并深度会减一,如果将这颗树上的每个结点都理解为一个区间,就比较好想了。
    可以用栈来模拟这个过程。
    详见代码:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    int a[N];
    int T, n;
    int sta[N], top, cnt;
    int node[N][2], sz[N];
    int f;
    void dfs(int rt, int l, int r) {
        if(rt == 0) return;
        int ls = node[rt][0], rs = node[rt][1];
        int mid = l + sz[ls] - 1;
        if(ls && rs) {
            if(f) cout << ' '; f = 1;
            cout << mid;
        }
        dfs(ls, l, mid); dfs(rs, mid + 1, r);
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> n;
            for(int i = 1; i <= n; i++) cin >> a[i];
            for(int i = 1; i <= 2 * n; i++) node[i][0] = node[i][1] = 0, sz[i] = 1;
            top = 0; cnt = n;
            for(int i = 1; i <= n; i++) {
                int x = i;
                while(top && a[sta[top]] == a[x]) {
                    node[++cnt][0] = sta[top];
                    node[cnt][1] = x;
                    sz[cnt] = sz[sta[top]] + sz[x];
                    a[cnt] = a[x] - 1;
                    x = cnt; --top;
                }
                sta[++top] = x;
            }
            if(top != 1 || a[cnt] != 0) {
                cout << "Impossible"  << '
    ';
            } else {
                f = 0;
                cout << "Possible" << '
    ';
                dfs(cnt, 1, n);
                cout << '
    ';
            }
        }
        return 0;
    }
    
  • 相关阅读:
    kibana简单使用
    全文检索 高亮显示 数据库同步添加
    算法: Reverse Bits 反转位收藏
    算法:Number of 1 Bits 收藏
    JS创建二维数组
    查询状态在1,2,5,7的记录-oracle
    oracle 修改某字段,判断是否为空,如果为空赋默认值
    在运筹学中什么样的解决方案是最优的
    项目开发失败
    筛选符合条件的数据后,如何做数据源,绑定repeater
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11392898.html
Copyright © 2011-2022 走看看