2020牛客暑期多校训练营(第六场)


A African Sort


B. Binary Vector

https://ac.nowcoder.com/acm/contest/5671/B

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int M = 2e7;
int n;

ll qpow(ll a, ll b) {
    a %= mod;
    ll res = 1;
    while (b) {
        if (b & 1)res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

ll res[M + 5];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    ll x = 1, y, z = 1, tmp = 1;
    ll Inv2 = qpow(2, mod - 2);
    res[0] = 0;
    for (int i = 1; i <= M; i++) {
        x = (x * 2) % mod;
        y = x - 1;
        z = (z * Inv2) % mod;
        tmp = (tmp * (y * z % mod)) % mod;
        res[i] = res[i - 1] ^ tmp;
    }
    int T;
    cin >> T;
    while (T--) {
        cin >> n;
        cout << res[n] << endl;
    }
    return 0;
}

C. Combination of Physics and Maths

https://ac.nowcoder.com/acm/contest/5671/C

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
ll a[N][N];
int n, m;
ll sum[N][N];

int main() {

    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);

        for (int i = 1; i <= n; i++) 
            for (int j = 1; j <= m; j++) 
                scanf("%lld", &a[i][j]);

        for (int i = 1; i <= n; i++) 
            for (int j = 1; j <= m; j++) 
                sum[i][j] = sum[i - 1][j] + a[i][j];
            
        double ans = 0.0;
        for (int i = 1; i <= n; i++) 
            for (int j = 1; j <= m; j++) 
                ans = max(ans, (1.0 * sum[i][j]) / (a[i][j] * 1.0));
            
        printf("%.8f\n", ans);
    }
    return 0;
}

D Data structure


E. Easy Construction

https://ac.nowcoder.com/acm/contest/5671/E

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    cin >> n >> k;

    if (n & 1) {//奇数
        if (k != 0) {
            puts("-1");
            return 0;
        } else {
            a[1] = n;
            for (int i = 2, j = 1; i <= n; i += 2, j++) {
                a[i] = j;
                a[i + 1] = n - j;
            }
        }
    } else {//n 偶数
        int tmp = (1 + n) * n / 2;
        if ((tmp % n) != k) {
            puts("-1");
            return 0;
        } else {
            a[1] = n;
            a[2] = n / 2;
            for (int i = 3, j = 1; i <= n; i += 2, j++) {
                a[i] = j;
                a[i + 1] = n - j;
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        cout << a[i] << ' ';
    }
    return 0;
}

F Fibonacci Partition


G. Grid Coloring

https://ac.nowcoder.com/acm/contest/5671/G

#include <bits/stdc++.h>
using namespace std;

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n, k;
        scanf("%d%d", &n, &k);
        if (n == 1 || k == 1 || (n + 1) * 2 * n % k != 0) {
            puts("-1");
            
        } else {
            int p = 0;
            for (int i = 1; i <= 2 * (n + 1); i++) {
                for (int j = 1; j <= n; j++) {
                    p = (p + 1) % k;
                    printf("%d ", p + 1);
                }
                if (n % k == 0)p = (p + 1) % k;
                printf("\n");
            }
        }
    }
    return 0;
}

H. Harmony Pairs · 数位dp

https://ac.nowcoder.com/acm/contest/5671/H

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int N = 1e3 + 10;
int sz;
int num[105];
ll dp[105][N * 2][2][2]; // dp[pos][sum][limB][limA]

// pos 当前位置
// sum B-A在前pos位上的差值总和
// limB limA B、A在当前位置枚举到的数位的限制 即 当前位是否可以枚举到数字9 0-不受限制 1-限制
ll dfs(int pos, int sum, int limB, int limA) {
    if (pos == sz) return sum > N ? 1 : 0;//为防止sum出现负数 在一开始时加上一个基础值
    if (~dp[pos][sum][limB][limA]) return dp[pos][sum][limB][limA];
    int upB = limB ? num[pos] : 9;
    ll res = 0;
    for (int i = 0; i <= upB; i++) {
        int upA = limA ? i : 9;// 题目要求 B>=A 且 S(A) > S(B)
        for (int j = 0; j <= upA; j++) {
            res = (res + dfs(pos + 1, sum + (j - i), limB && i == upB, limA && j == upA)) % mod;
        }
    }
    return dp[pos][sum][limB][limA] = res;
}

ll solve(string s) {
    sz = s.length();
    for (int i = 0; i < sz; i++) {
        num[i] = s[i] - '0';
    }
    // 从最高位开始
    return dfs(0, N, 1, 1);
}

string s;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    memset(dp, -1, sizeof dp);
    
    cin >> s;
    cout << solve(s) << endl;
    return 0;
}

I Interesting Stiriling


J. Josephus Transform · 线段树 + 置换

https://ac.nowcoder.com/acm/contest/5671/J

官方题解
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int n, m, k, x;
int p[N], nxt[N];

struct segTree {
#define ls (o<<1)
#define rs (o<<1|1)

    struct node {
        int l, r;
        int sum;
    } t[N << 2];

    void pushup(int o) {
        t[o].sum = t[ls].sum + t[rs].sum;
    }

    //不带数组 建树
    void build(int o, int l, int r) {
        t[o].l = l;
        t[o].r = r;
        if (l == r) {
            t[o].sum = 1;
            return;
        }
        int mid = l + r >> 1;
        build(ls, l, mid);
        build(rs, mid + 1, r);
        pushup(o);
    }

    //找当前线段树里存在的第k个数
    // 同时 删除这个数
    int query(int o, int L, int R, int k) {
        if (t[o].l == t[o].r) {
            t[o].sum = 0;
            return t[o].l;
        }
        int res;
        if (t[ls].sum >= k) res = query(ls, L, R, k);
        else res = query(rs, L, R, k - t[ls].sum);
        pushup(o);
        return res;
    }
} ST;

int vis[N];
vector<int> v;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        a[i] = i;
    }
    while (m--) {
        cin >> k >> x;
        for (int i = 1; i <= n; i++) {
            vis[i] = 0;
        }

        ST.build(1, 1, n);
        for (int i = 1, pos = k; i <= n; i++) {
            p[i] = ST.query(1, 1, n, pos);
            // 下一位数是剩下的数字里面的第pos位
            if (i < n) pos = ((pos - 1 + k - 1) % (ST.t[1].sum)) + 1;
        }

		// 根据置换p 求出环内的顺序
        for (int i = 1; i <= n; i++) {
            nxt[p[i]] = i;
        }

        for (int i = 1, now; i <= n; i++) {
            if (!vis[i]) {
                v.clear();
                now = i;
                while (!vis[now]) {
                    vis[now] = 1;
                    v.push_back(now);
                    now = nxt[now];
                }

                // 置换群快速幂
                for (int j = 0, sz = v.size(); j < sz; j++) {
                    p[v[j]] = v[(j + x) % sz];//新的位置关系
                }
            }
        }

        for (int i = 1; i <= n; i++) {
            nxt[p[i]] = a[i];
        }

        for (int i = 1; i <= n; i++) {
            a[i] = nxt[i];
        }
    }

    for (int i = 1; i <= n; i++) {
        cout << a[i] << (i == n ? "\n" : " ");
    }
    
    return 0;
}

K. K-Bag

https://ac.nowcoder.com/acm/contest/5671/K
参考博客 2020牛客多校第六场K.K-Bag (思维?)

#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
int a[N], b[N], vis[N], vis_pos[N];
int mp[N];
int n, k;

namespace Discretization { // 离散化板子
    vector<int> backUp;

    void discrete() {
        sort(backUp.begin(), backUp.end());
        backUp.erase(unique(backUp.begin(), backUp.end()), backUp.end());
    }

    int id(int x) {
        return lower_bound(backUp.begin(), backUp.end(), x) - backUp.begin() + 1;
    }
}
using namespace Discretization;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int T;
    cin >> T;
    while (T--) {
        cin >> n >> k;
        int flag = 1;//判断标志
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            if (a[i] > k) {
                flag = 0;
            }
            backUp.push_back(a[i]);//离散化
        }

        if (!flag) {
            cout << "NO" << endl;
            backUp.clear();
            continue;
        }

        discrete();//离散化

        for (int i = 1; i <= n; i++) {
            a[i] = id(a[i]);// 离散化后的下标
        }

        if (n > k) {
            flag = 0;

            // 先判断 a[r-k+1...r]里 数值为a[r] 的个数是否多次出现
            for (int r = 1, l = 1; r <= n; r++) {
                vis[a[r]]++;
                if (vis[a[r]] > 1) {
                    b[r] = 0;// b[r]表示 以r为结尾 区间长度为k的 一个区间划分 是否合法 0非法 1合法
                } else {
                    b[r] = 1;
                }
                if (r >= k) {
                    vis[a[l++]]--;
                }
            }

            // 但是 还有 a[r-k+1...r]里  重复数字不是在a[r]上出现的 非法划分
            // 比如 k=6时 [x 3 2 3 x x] 显然 [x x x 3 2 3] [ x 3 2 3 x x] [ 3 2 3 x x x] 都是不合法的
            // 但是 前面统计的时候 只有第2个3的位置 即[x x x 3 2 3] 被设置为了非法 其后面的位置都被当成了合法
            
            // 所以 需要将 这些包含 重复数字的区间划分 设置为非法
            int cnt = 0;// cnt表示 还有cnt个位置需要设置为非法
            for (int i = 1; i <= n; i++) {
                if (mp[a[i]]) {
                    cnt = max(cnt, k - (i - mp[a[i]]));
                    // 同时包含位置i 和上一次出现的位置 mp[a[i]] 的长度为k的区间 都是非法的
                }
                if (cnt) {
                    b[i] = 0;
                    cnt--;
                }
                mp[a[i]] = i;
            }
            // 特殊情况 
            // 补齐最后非法的区间 即 最后一段区间是非法 但是只出现了非法区间的一部分
            // 不过 这一部分b[i]本来就是0 就不写了

            for (int i = 1, lim = n + cnt; i <= lim; i++) {
                if (b[i] == 0) {
                    vis_pos[(i % k) + 1] = 1;
                    // 那么从当前位置开始k个为一组往前分组 一直分到到第一个含有k个元素的组
                    // 都是不合法的分法 将第一个位置的下标 标记为1 - 不合法
                }
            }

            for (int i = 1; i <= k; i++) {
                if (vis_pos[i] == 0) {
                    //存在合法的起始位置
                    flag = 1;
                    break;
                }
            }

        } else {
            // 当n<=k 要么只有一组 要么是两组(即各取一部分拼接形成)
            // 找到第一个重复的位置
            int pos = 0;
            for (int i = 1; i <= n; i++) {
                vis[a[i]]++;
                if (vis[a[i]] > 1) {
                    pos = i;
                    break;
                }
            }
            // 都没有重复 显然合法
            if (pos == 0) {

            } else {
                // 以pos为分界线划分前后两部分
                // 将前面合法的统计过的部分清空
                for (int i = 1; i <= pos; i++) {
                    vis[a[i]]--;
                }
                // 判断后面的是否合法
                for (int i = pos; i <= n; i++) {
                    vis[a[i]]++;
                    if (vis[a[i]] > 1) {
                        flag = 0;
                        break;
                    }
                }
            }
        }
        
        if (flag) {
            cout << "YES" << endl;
        } else {
            cout << "NO" << endl;
        }

        // init
        memset(b, 0, sizeof b);
        memset(vis, 0, sizeof vis);
        memset(vis_pos, 0, sizeof vis_pos);
        memset(mp, 0, sizeof mp);

        backUp.clear();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值