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=1∑D(xi−x)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 0∼i的袋子中放入了方案 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 Li∼Ri上的一个点(概率均等) 
-  将这个点修改为 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×lenlen−1+Xi×len1
对于每次操作,可以先对区间乘上 l e n − 1 l e n \frac{len - 1}{len} lenlen−1,再加上 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,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。




















