AtCoder Beginner Contest 332 A~F

A.Online Shopping(计算)

题意:

需要购买 N N N种物品,第 i i i种物品的价格为 P i P_i Pi,且第 i i i件物品需买 Q i Q_i Qi件。

商店满 S S S元包邮,不满则需支付 K K K元邮费,问需支付多少钱。

分析:

按照要求计算,如果商品总价没到包邮门槛,那么就加上邮费。

代码:

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 3e5 + 5;

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    LL ans = 0;
    for (int i = 1; i <= n; i++) {
        int a, b;
        cin >> a >> b;
        ans += a * b;
    }
    if (ans < m) ans += k;
    cout << ans << endl;
}

int main() {
    solve();
    return 0;
}

B.Glass and Mug(模拟)

题意:

有两个杯子,一个玻璃杯,一个马克杯,每个杯子都有一个容量,且保证马克杯的容量比玻璃杯大。

你需要按以下要求进行 K K K次操作:

  • 如果玻璃杯装满水,将玻璃杯中水倒完

  • 如果玻璃没有装满水,且马克杯是空的,往马克杯里加满水

  • 否则,将马克杯里的水倒入玻璃杯中,直到马克杯空或玻璃杯满

分析:

按题目要求模拟即可。

代码:

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 3e5 + 5;

void solve() {
    int k, g, m;
    cin >> k >> g >> m;
    int glass = 0, mug = 0;
    while (k--) {
        if (glass == g) {
            glass = 0;
        } else if (mug == 0) {
            mug = m;
        } else {
            int add = min(mug, g - glass);
            glass += add;
            mug -= add;
        }
    }
    cout << glass << ' ' << mug << endl;
}

int main() {
    solve();
    return 0;
}

C.T-shirts(模拟)

题意:

给出一个长度为 N N N的字符串,字符串第 i i i个字符含义如下:

  • 0:在家休息(不需要穿T恤,且会把所有穿过的T恤洗掉,下一天所有T恤均可穿)

  • 1:出门吃饭(可以选择穿纯色T恤或带logo的T恤)

  • 2:参加比赛(必须选择带logo的T恤)

开始时拥有 M M M件纯色的T恤,问至少拥有多少件带logo的T恤才能保证这 N N N天都有衣服穿?

分析:

按要求进行模拟,如果休息就将穿过的T恤清空,如果出门吃饭,有纯色T恤就选纯色,没有就选带logo的,参加比赛也选择带logo的,记录过程中,最多穿过的带logo的T恤数量即可。

代码:

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 3e5 + 5;

void solve() {
    int n, m;
    string s;
    cin >> n >> m >> s;
    int plain = 0, logo = 0, ans = 0;
    for (int i = 0; i < n; i++) {
        if (s[i] == '0') {
            plain = logo = 0;
        } else if (s[i] == '1') {
            if (plain < m) {
                plain++;
            } else {
                logo++;
            }
        } else {
            logo++;
        }
        ans = max(ans, logo);
    }
    cout << ans << endl;
}

int main() {
    solve();
    return 0;
}

D.Swapping Puzzle(枚举全排列)

题意:

给出两个 H × W H \times W H×W的网格 A A A B B B,每次可以选择一个数字 i i i进行以下两种操作之一:

  • 交换第 i i i行和第 i + 1 i + 1 i+1行所有元素

  • 交换第 i i i列和第 i + 1 i + 1 i+1列所有元素

问:输出将网格A变为网格B的最少操作次数,如果无法完成,输出出-1

分析:

行列的交换是互相不会产生影响的,因此可以使用两层循环使用全排列函数next_permutation对行列的最终排列进行枚举,然后检查是否满足题意,记录最小的交换次数即可。

Tips:不难发现,行列的交换次数为序列中逆序对数量,如序列{1, 3, 2, 5, 4},里面存在两对逆序对 ( 3 , 2 ) , ( 5 , 4 ) (3, 2), (5, 4) (3,2),(5,4),因此该序列是经过两次交换得到的。

代码:

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 3e5 + 5;

int row[10] = {0, 1, 2, 3, 4, 5}, colum[10] = {0, 1, 2, 3, 4, 5};
int n, m, a[10][10], b[10][10];

int getCost() {
    int res = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            if (row[i] > row[j]) {
                res++;
            }
        }
    }
    for (int i = 1; i <= m; i++) {
        for (int j = i + 1; j <= m; j++) {
            if (colum[i] > colum[j]) {
                res++;
            }
        }
    }
    return res;
}

bool check() {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (a[row[i]][colum[j]] != b[i][j]) {
                return false;
            }
        }
    }
    return true;
}

void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> b[i][j];
        }
    }
    do {
        do {
            if (check()) {
                cout << getCost() << endl;
                return;
            }
        } while (next_permutation(colum + 1, colum + m + 1));
    } while (next_permutation(row + 1, row + n + 1));
    cout << -1 << endl;
}

int main() {
    solve();
    return 0;
}

E.Lucky bag(二进制枚举,DP)

题意:

N N N件物品, M M M个幸运袋,每个物品均需被装入到一个袋子中,然后要求找到所有装入方案中方差最小中一种,即使 V V V最小:

  • V = 1 D ∑ i = 1 D ( x i − x ‾ ) 2 V = \frac{1}{D}\sum\limits_{i = 1}^{D}(x_i - \overline{x})^{2} V=D1i=1D(xix)2

其中 x i x_i xi为第 i i i个袋子中的物品价值总和, x ‾ \overline{x} x为袋子的平均价值。

分析:

可以先预处理一个幸运袋中所有的物品装入方案,使用二进制枚举进行。

然后使用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示 0 ∼ i 0 \sim i 0i的袋子中放入了方案 j j j包含的物品的最小方差。

每一次计算 d p [ i ] [ j ] dp[i][j] dp[i][j]时,通过枚举所有方案 j j j的子集,并将当前袋子装入剩下部分的子集,使得两个子集刚好可以拼成方案 j j j,记录最小的方差。

Tips:枚举子集如果每次均通过减一的方式,时间复杂度就会变得很高,而每次减一后,再通过与当前枚举的集合 j j j进行与运算,既保证了当前方案为集合 j j j的子集,且只从上一个方案减一,即、也取到了下一个最大的方案。

代码:

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 3e5 + 5e4;

int n, m;

double w[20];
double s[N];
double dp[20][N];

void solve() {
    double avg = s[(1 << n) - 1] / m;
    for (int i = 0; i < m; i++) {
        for (int j = (1 << n) - 1; j >= 0; j--) {
            if (i == 0) {
                dp[i][j] = (s[j] - avg) * (s[j] - avg);//第一个幸运袋,直接放入
            } else {
                dp[i][j] = dp[i - 1][j] + avg * avg;//先假设当前袋子不放东西(需加上(avg - 0)^2)
                for (int k = j; k; k = (k - 1) & j) {//枚举子集
                    dp[i][j] = min(dp[i][j], dp[i - 1][j ^ k] + (s[k] - avg) * (s[k] - avg));//dp[0][k]可换为(s[k] - avg)^2
                }
            }
        }
    }
    cout << fixed << setprecision(15) << dp[m - 1][(1 << n) - 1] / m << endl;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) cin >> w[i];
    for (int i = (1 << n) - 1; i >= 0; i--) {
        for (int j = 0; j < n; j++) {
            if ((i >> j) & 1) {
                s[i] += w[j];
            }
        }
    }
    solve();
    return 0;
}

F.Random Update Query(线段树,期望)

题意:

给出一个长度为 N N N的数组 A = A 1 , A 2 , . . . , A N A = A_1, A_2, ..., A_N A=A1,A2,...,AN

对这个数组进行以下操作 M M M次:

  • 随机的选择区间 L i ∼ R i L_i \sim R_i LiRi上的一个点(概率均等)

  • 将这个点修改为 X i X_i Xi

问结果 M M M次操作后,每个位置上的数字的期望是多少。

分析:

随机的选择区间上的数字服从于均匀分布,则每个数字被修改的概率为 1 len \frac{1}{\text{len}} len1,其中 l e n len len为区间内的数字个数。那么对于每个数字来说实际上服从于 01 01 01分布,即 A i A_i Ai的期望为:

  • E ( A i ) = A i × l e n − 1 l e n + X i × 1 l e n E(A_i) = A_i \times \frac{len - 1}{len} + X_i \times \frac{1}{len} E(Ai)=Ai×lenlen1+Xi×len1

对于每次操作,可以先对区间乘上 l e n − 1 l e n \frac{len - 1}{len} lenlen1,再加上 X i × 1 l e n X_i \times \frac{1}{len} Xi×len1,区间操作使用线段树辅助实现。

注意:

  • 除法转为乘法逆元

  • 双标记先推乘法,再推加法

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 998244353;
const int N = 2e5 + 5e2;

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

LL n, m, E[N << 2], add[N << 2], mul[N << 2];

void pushup(int x) {
    E[x] = (E[x << 1] + E[x << 1 | 1]) % MOD;
}

void build (int l, int r, int x) {
    if (l == r) {
        cin >> E[x];
        return;
    }
    mul[x] = 1;
    int mid = (l + r) >> 1;
    build(l, mid, x << 1);
    build (mid + 1, r, x << 1 | 1);
    pushup(x);
}

void pushdown(int l, int r, int x) {
    add[x << 1] = (add[x << 1] * mul[x] % MOD + add[x]) % MOD;
    add[x << 1 | 1] = (add[x << 1 | 1] * mul[x] % MOD + add[x]) % MOD;
    mul[x << 1] = (mul[x << 1] * mul[x]) % MOD;
    mul[x << 1 | 1] = (mul[x << 1 | 1] * mul[x]) % MOD;
    LL mid = (l + r) >> 1;
    E[x << 1] = (E[x << 1] * mul[x] % MOD + add[x] * (mid - l + 1) % MOD) % MOD;
    E[x << 1 | 1] = (E[x << 1 | 1] * mul[x] % MOD + add[x] * (r - mid) % MOD) % MOD;
    mul[x] = 1;
    add[x] = 0;
}

void update(int l, int r, int x, int ul, int ur, int op, LL val) {
    if (l >= ul && r <= ur) {
        if (op) {//乘法
            mul[x] = (mul[x] * val) % MOD;
            add[x] = (add[x] * val) % MOD;
            E[x] = (E[x] * val) % MOD;
        } else {//加法
            add[x] = (add[x] + val) % MOD;
            E[x] = (E[x] + val * (r - l + 1) % MOD) % MOD;
        }
        return;
    }
    pushdown(l, r, x);
    int mid = (l + r) >> 1;
    if (ul <= mid) update(l, mid, x << 1, ul, ur, op, val);
    if (ur > mid) update(mid + 1, r, x << 1 | 1, ul, ur, op, val);
}

void print(int l, int r, int x) {
    if (l == r) { if (l != 1) cout << ' ';
        cout << E[x];
        return;
    }
    pushdown(l, r, x);
    int mid = (l + r) >> 1;
    print(l, mid, x << 1);
    print(mid + 1, r, x << 1 | 1);
}

int main () {
    cin >> n >> m;
    build(1, n, 1);
    while (m--) {
        LL l, r, x;
        cin >> l >> r >> x;
        LL num = (r - l) * qpow(r - l + 1, MOD - 2) % MOD;
        update(1, n, 1, l, r, 1, num);
        num = x * qpow(r - l + 1, MOD - 2) % MOD;
        update(1, n, 1, l, r, 0, num);
    }
    print(1, n, 1);
    return 0;
}

学习交流

以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。

  • 18
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值