浙江农林大学蓝桥杯程序设计竞赛校选拔赛(同步赛)【题解】

根据我的主观感受排序

H:蒟蒻wzc的填数游戏
知识点:模拟暴力
模拟就完事了

#include <bits/stdc++.h>
using namespace std;
char s[30000];
int g[10][10];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
inline void solve() {
    memset(g, 0, sizeof g);
    int n; cin >> n;
    unordered_map<char, int> f;
    f['w'] = 0, f['d'] = 1, f['s'] = 2, f['a'] = 3;
    scanf("%s", s + 1);
    int cnt = 1;
    g[1][1] = 1;
    int x = 1, y = 1;
    for (int i = 1; s[i]; i ++ ) {
        int tmp = f[s[i]];
        x = x + dx[tmp], y = y + dy[tmp];
        g[x][y] = ++ cnt;
    }
    for (int i = 1; i <= n; i ++ ) {
        for (int j = 1; j <= n; j ++ ) cout << g[i][j] << ' ';
        cout << endl;
    }
    cout << endl;
}
int main() {
    int t; cin >> t;
    while (t -- ) {
        solve();
    }
    return 0;
}

A:uu与糖果
知识点:模拟暴力
模拟就完事了,注意一下h为0的特判即可

#include <bits/stdc++.h>
using namespace std;
const int N = 2000020;
typedef long long LL;
LL a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    LL n, k, h; cin >> n >> k >> h;
    LL res = 0;
    if (h == 0) {
        for (int i = 1; i <= n; i ++ ) cin >> a[i], res += a[i];
        cout << res << endl;
    } else {
        for (int i = 1; i <= n; i ++ ) {
            cin >> a[i];
            if (a[i] > k) res += a[i];
            else {
                LL re = k - a[i];
                a[i] += re / h * h;
                res += a[i];
            }
        }
        res += h;
        cout << res << endl;
    }

    return 0;
}

F:签不上到?做这题就对了
知识点:模拟暴力
将数中所有数位拆出来,然后从高位到低位遍历,如果之后有比当前大的,就取最大的最后的那个数字交换即可
其实只要判断大于0就行了,这里是考虑全了的写法

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
inline void solve() {
    LL n; cin >> n;
    LL tmp = n;
    vector<int> v;
    if (tmp == 0) {
        cout << 0 << endl;
    } else if (tmp > 0) {
        while (tmp) v.push_back(tmp % 10), tmp /= 10;
        reverse(v.begin(), v.end());
        for (int i = 0; i < v.size(); i ++ ) {
            int ma = v[i], poi = i;
            for (int j = i; j < v.size(); j ++ ) {
                if (ma <= v[j] && v[j] != v[i]) {
                    ma = v[j];
                    poi = j;
                }
            }
            if (poi == i) continue;
            else {
                swap(v[poi], v[i]);
                break;
            }
        }
        for (auto ite : v) cout << ite;
        cout << endl;
    } else {
        tmp = -tmp;
        while (tmp) v.push_back(tmp % 10), tmp /= 10;
        reverse(v.begin(), v.end());
        for (int i = 0; i < v.size(); i ++ ) {
            int mi = v[i], poi = i;
            for (int j = i; j < v.size(); j ++ ) {
                if (mi >= v[j] && v[j] != v[i]) {
                    mi = v[j];
                    poi = j;
                }
            }
            if (poi == i) continue;
            else {
                swap(v[poi], v[i]);
                break;
            }
        }
        cout << '-';
        bool is = false;
        for (auto ite : v) {
            if (ite == 0 && is == false) {
                is = true;
                continue;
            } else cout << ite;
        }
        cout << endl;
    }
}
int main() {
    int t; cin >> t;
    while (t -- ) {
        solve();
    }
    return 0;
}

B:蒟蒻wzc与方块涂色
知识点:二进制搜索
CF有一道差不多的题目:B. Berland Crossword
CF上是平面,这里是三维,其实是一个道理,我们为了讲解方便,拿一个平面来举例子,其他的面同理
设一个面的四条边分别要涂a,b,c,d块黑色,我们很容易发现拐角的那个方块比较难处理,于是我们直接二进制搜索,用一个四位的二进制数的每一位来表示是否要填,填完后我们验证即可
所以当每条边减去角上的点过后应该大于等于0并且小于等于n - 2,否则不合法,检查下一状态是否合法
这是平面状态下的,如果是立体的,就一个一个面判断即可
一共8个顶点,从\((00000000)_2\)枚举到\((11111111)_2\)一共\(2^8\)的复杂度,加上1000组数据,不会超时

#include <bits/stdc++.h>
using namespace std;
int a[20];
int n;
inline bool change(int p1, int p2, int p3, int p4, int l1, int l2, int l3, int l4) {
    if (p1 == 1) l1 -- , l4 -- ;
    if (p2 == 1) l1 -- , l2 -- ;
    if (p3 == 1) l2 -- , l3 -- ;
    if (p4 == 1) l3 -- , l4 -- ;
    if (l1 < 0 || l2 < 0 || l3 < 0 || l4 < 0) return false;
    if (l1 > n - 2 || l2 > n - 2 || l3 > n - 2 || l4 > n - 2) return false;
    return true;
}
inline bool check(int x) {
    vector<int> v;
    v.push_back(123);
    for (int i = 0; i < 8; i++) v.push_back(x >> i & 1);
    //第一面
    if (!change(v[1], v[2], v[3], v[4], a[1], a[2], a[3], a[4])) return false;
    //第二面
    if (!change(v[1], v[8], v[5], v[4], a[7], a[6], a[5], a[4])) return false;
    //第三面
    if (!change(v[8], v[7], v[6], v[5], a[8], a[9], a[10], a[6])) return false;
    //第四面
    if (!change(v[2], v[7], v[6], v[3], a[11], a[9], a[12], a[2])) return false;
    //第五面
    if (!change(v[8], v[7], v[2], v[1], a[8], a[11], a[1], a[7])) return false;
    //第六面
    if (!change(v[5], v[6], v[3], v[4], a[10], a[12], a[3], a[5])) return false;
    return true;
}
inline void solve() {
    cin >> n;
    for (int i = 1; i <= 12; i ++ ) cin >> a[i];
    for (int i = 0; i < (1 << 8); i ++ ) {
        if (check(i)) {
            puts("YES");
            return ;
        }
    }
    puts("NO");
}
int main() {
    int t; cin >> t;
    while (t -- ) solve();
    return 0;
}

J:消消乐
知识点:模拟暴力(链表)
用链表模拟即可
这里采用队友acrn的代码

#include <iostream>
using namespace std;

int a[120], b[120];

int main() {
    int t;
    cin >> t;
    while(t--) {
        int n, k, x;
        int the_max = 0;
        cin >> n >> k >> x;
        for(int i = 1; i <= n; i++) cin >> a[i];
        for(int i = 1; i <= n - 1; i ++) {
            if(!(a[i] == a[i + 1] && a[i] == x)) continue;
            //cout << i << 'c' << endl;
            for(int j = 1; j <= i; j++) {
                b[j] = a[j];
            }
            b[i + 1] = x;
            for(int j = i + 2; j <= n + 1; j++) {
                b[j] = a[j - 1];
            }
            b[n + 2] = 0;
            //for(int i = 1; i <= n + 1; i++) cout << b[i] << ' ';cout << endl;
            int head = i, tail = i + 2, cnt = 0, col = x, col1 = x, col2 = x, flag = 1;
            while(head >= 1 && tail <= n + 1) {
                //cout << col1 << ' ' << col2 << endl;
                if(col1 != col2) break;
                //cout << col1 << ' ' << col2 << endl;
                col = col1; head--, tail++;
                while(b[head] == col && head >= 1) {
                    cnt++; head--;
                    flag = 1;
                    //cout << 'b' << '+' << endl;
                }
                col1 = b[head];
                while(b[tail] == col && tail <= n + 1) {
                    cnt++; tail++;
                    flag = 1;
                    //cout << 'c' << '+' << endl;
                }
                col2 = b[tail];
                if(flag == 1) cnt += 2;
                //cout << "+" << endl;
                else break;
                flag = 0;
            }
            //cout << cnt << endl;
            if(cnt > the_max) the_max = cnt;
        }
        cout << the_max << endl;
    }
}

I:集训队长魂牵梦绕的题目
知识点:模拟
这题在CF上也有一个差不多的:B. Minimal Cost
不过思想很简单:我们把可以阻挡去路的两个点组叫做障碍,我们只需要在每次修改过后查看障碍个数即可

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 20;
int a[N], b[N];

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int n; cin >> n;
    int cnt = 0;//记录障碍个数
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    for (int i = 1; i <= n; i ++ ) cin >> b[i];
    for (int i = 2; i <= n; i ++ ) {
        if (a[i] == 0) continue;
        if (b[i - 1] == 1) cnt ++ ;
        if (b[i] == 1) cnt ++ ;
        if (b[i + 1] == 1) cnt ++ ;
    }
    int q; cin >> q;
    if (cnt == 0) cout << "I'm the worst friend.Please forgive me." << endl;
    else cout << "You are the worst friend." << endl;
    while (q -- ) {
        int x, y; cin >> x >> y;
        if (x == 1) {
            if (a[y] == 1) {
                if (b[y] == 1) cnt -- ;
                if (b[y - 1] == 1) cnt -- ;
                if (b[y + 1] == 1) cnt -- ;
                a[y] = 0;
            } else {
                if (b[y] == 1) cnt ++ ;
                if (b[y - 1] == 1) cnt ++ ;
                if (b[y + 1] == 1) cnt ++ ;
                a[y] = 1;
            }
        } else {
            if (b[y] == 1) {
                if (a[y] == 1) cnt -- ;
                if (a[y - 1] == 1) cnt -- ;
                if (a[y + 1] == 1) cnt -- ;
                b[y] = 0;
            } else {
                if (a[y] == 1) cnt ++ ;
                if (a[y - 1] == 1) cnt ++ ;
                if (a[y + 1] == 1) cnt ++ ;
                b[y] = 1;
            }
        }
        if (cnt == 0) cout << "I'm the worst friend.Please forgive me." << endl;
        else cout << "You are the worst friend." << endl;
    }
    return 0;
}

G:来上数学课
知识点:贪心
我们通过自己模拟可以发现只要在它即将加倍的那一题让他答错,并且是在尽量后面的题目中答错即可,知道了这个策略我们写出代码也不难了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 9;
inline LL q_pow(LL a, LL b) {
    LL ans = 1;
    for (; b; b >>= 1) {
        if (b & 1) ans = ans * a % mod;
        a = a * a % mod;
    }
    return ans;
}
int main() {
    int n, m, k;
    while (cin >> n >> m >> k) {
        LL cnt = n / k;
        if (cnt <= n - m) cout << m << endl;
        else {
            LL tmp = cnt - n + m;
            LL ans = k * (q_pow(2, tmp + 1) - 2 + mod) % mod;
            ans = (ans + m - (k * (cnt - n + m))) % mod;
            cout << ans << endl;
        }
    }
    return 0;
}

C:排列组合之王
知识点:位运算
位运算中我们只需要计算每一个二进制数位上的贡献即可,因此我们用一个数m来记下每位是否有出现1,因为有1就有贡献,没有1的话永远也不会有1
最后通过打表发现在这样的情况下每位的贡献就是\(2^{n-1}\),暂时不知道如何证明(蹲一个大佬)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mmod = 1e9 + 7;
inline LL q_pow(LL a, LL b, int mod) {
    LL ans = 1;
    for (; b; b >>= 1) {
        if (b & 1) ans = ans * a % mod;
        a = a * a % mod;
    }
    return ans;
}
int main() {
    int n; cin >> n;
    LL m = 0;
    for (int i = 1; i <= n; i ++ ) {
        int x; cin >> x;
        m |= x;
    }
    LL pp = q_pow(2, n - 1, mmod);
    cout << m * pp % mmod;
    return 0;
}

D:异或游戏
知识点:Trie,前缀和
就是个板子题,然后用前缀和来维护前缀的最小值,后缀和来维护后缀的最大值(这里的前缀后缀和都指异或和)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 6000060;
struct node {
    LL cnt = 0, ch[N][2];
    void input(LL x) {
        int u = 0;
        for (int i = 31; ~i; i -- ) {
            LL c = (x >> i) & 1;
            if (!ch[u][c]) ch[u][c] = ++ cnt;
            u = ch[u][c];
        }
    }
    LL find_same(LL x) {
        LL ans = 0, u = 0;
        for (int i = 31; ~i; i -- ) {
            LL c = (x >> i) & 1, cc = c ^ 1;
            if (ch[u][c]) u = ch[u][c], ans <<= 1;
            else u = ch[u][cc], ans = ans << 1 | 1;
        }
        return ans;
    }
    LL find_another(LL x) {
        LL ans = 0, u = 0;
        for (int i = 31; ~i; i -- ) {
            LL c = (x >> i) & 1, cc = c ^ 1;
            if (ch[u][cc]) u = ch[u][cc], ans = ans << 1 | 1;
            else u = ch[u][c], ans <<= 1;
        }
        return ans;
    }
} trie1, trie2;
LL sum_pre[N], sum_last[N];
LL l[N], r[N];
int main() {
    int n; cin >> n;
    for (int i = 1; i <= n; i ++ ) {
        cin >> sum_pre[i];
        sum_last[i] = sum_pre[i];
        sum_pre[i] ^= sum_pre[i - 1];
        l[i] = 1e18;
    }
    l[0] = 1e18;
    for (int i = n; i; i -- ) sum_last[i] ^= sum_last[i + 1];
    for (int i = 1; i <= n; i ++ ) {
        trie1.input(sum_pre[i - 1]);
        l[i] = trie1.find_same(sum_pre[i]);
        l[i] = min(l[i], l[i - 1]);
    }
    for (int i = n; i >= 1; i -- ) {
        trie2.input(sum_last[i + 1]);
        r[i] = trie2.find_another(sum_last[i]);
        r[i] = max(r[i], r[i + 1]);
    }
    LL ans = -1e18;
    for (int i = 1; i < n; i ++ ) ans = max(ans, r[i + 1] - l[i]);
    cout << ans << endl;
    return 0;
}

E:谁是天选之人
知识点:线段树
其实就是个线段树的板子题,当然这里还有另外一种解法, 用并查集维护
这里感谢SaladDays提供的思路
基本思路就是在这个序列中,每次划定的范围中,一个数是它右边的数的儿子。而我们填数由于以最后一次为准, 所以直接从后往前填即可,这里保证了每个点只遍历到一遍,所以复杂度是O(n)的,具体看代码。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000100;
int par[N], ans[N];
inline int find(int x) {
    if (x == par[x]) return x;
    return par[x] = find(par[x]);
}
int main() {
    int n, m, a, b; cin >> n >> m >> a >> b;
    for (int i = 1; i <= n; i ++ ) par[i] = i;
    for (int i = m; i; i -- ) {
        int l = (i * a + b) % n + 1, r = (i * b + a) % n + 1;
        if (l > r) swap(l, r);
        int root = find(l);
        while (root <= r) {
            par[root] = root + 1;
            ans[root] = i;
            root = find(root);
        }
    }
    for (int i = 1; i <= n; i ++ ) cout << ans[i] << endl;
    return 0;
}
posted @ 2021-03-10 16:43  Time_Limit_Exceeded  阅读(140)  评论(0编辑  收藏  举报