2023杭电暑假多校4 题解 | JorbanS

3-Simple Set Problem

题意 k 个多重集合,每个集合选出一个数形成新集合A,求 m a x ( A ) − m i n ( A ) max(A)-min(A) max(A)min(A)

题解 排序后,滑动窗口,保证窗口里有 k 个集合的数,答案取 m i n min min 即可

#include <iostream>
#include <vector>
#include <algorithm>
#include <deque>
#define endl '\n'
#define aa first
#define bb second

using namespace std;
typedef pair<int, int> pii;
const int N = 1e6 + 2;
int n, k, c, cnt[N];

void solve() {
    n = 0;
    vector<pii> v;
    cin >> k;
    for (int i = 0; i < k; i ++) cnt[i] = 0;
    for (int i = 0; i < k; i ++) {
        cin >> c;
        n += c;
        while (c --) {
            int x; cin >> x;
            v.push_back({x, i});
        }
    }
    sort(v.begin(), v.end());
    deque<int> dq;
    int count = 0, idx = 0, res = 2e9;
    while (idx < n) {
        if (!cnt[v[idx].bb]) count ++;
        dq.push_front(idx);
        cnt[v[idx].bb] ++;
        idx ++;
        if (count < k) continue;
        while (cnt[v[dq.back()].bb] > 1) {
            cnt[v[dq.back()].bb] --;
            dq.pop_back();
        }
        res = min(res, v[dq.front()].aa - v[dq.back()].aa);
    }
    cout << res << endl;
}

int main() {
    cin.tie(0) -> sync_with_stdio(false);
    int T; cin >> T;
    while (T --) solve();
    return 0;
}

6-PSO

题意 菊花图,求每个点到别的点需要经过的边数的期望和最大值

题解
期望 = 所有组合经过的边数 组合数量 = 叶节点之间的边数 × 2 + 中心节点连着的叶节点数 叶节点之间的组合数 + 中心节点连着的叶节点数 期望=\frac{所有组合经过的边数}{组合数量}=\frac{叶节点之间的边数×2+中心节点连着的叶节点数}{叶节点之间的组合数+中心节点连着的叶节点数} 期望=组合数量所有组合经过的边数=叶节点之间的组合数+中心节点连着的叶节点数叶节点之间的边数×2+中心节点连着的叶节点数

2 × C n − 1 2 + n − 1 C n − 1 2 + n − 1 \frac{2×C_{n-1}^2+n-1}{C_{n-1}^2+n-1} Cn12+n12×Cn12+n1

最大值除了 n = 2 n=2 n=2 时最大值为 1,其余时候最大值为 2

#include <iostream>
#include <cmath>

using namespace std;
typedef long long ll;
ll n;

void solve() {
    cin >> n;
    double res = pow(n - 1, 2);
    double t = (n - 2) * (n - 1) / 2 + (n - 1);
    printf("%.9lf %.9lf\n", res / t, n == 2 ? 1.0 : 2.0);
}

int main() {
    int T; cin >> T;
    while (T --) solve();
    return 0;
}

10-Kong Ming Qi

题意 ( n + 2 ) × ( m + 2 ) (n+2)×(m+2) (n+2)×(m+2) 的棋盘,四周一圈没棋子,其余填满,若上下左右有一个棋子,可以跳到一个棋子对面(前提是本来没棋

子),并且吃掉这一个棋子

在这里插入图片描述

题解 不妨设 m ≤ n m ≤ n mn,再分类讨论:(此题解参考官方题解)

  • m = 1 m = 1 m=1 时,答案显然为 ⌈ m / 2 ⌉ ⌈m/2⌉ m/2
  • n ≥ 5 n ≥ 5 n5 时,可证明: n × m n × m n×m 等价于 ( n − 2 ) × m (n-2) × m (n2)×m

可都转换成 2 ≤ n ≤ m ≤ 4 2 ≤ n ≤ m ≤ 4 2nm4 的情形,再简单手玩得到答案

d e f def def 基本操作, n ≥ 3 n ≥ 3 n3 时,一次消去三格,可以将 n × m n × m n×m 转化为 ( n − 3 ) × m (n - 3) × m (n3)×m

在这里插入图片描述

n = 2 n = 2 n=2 时,可以将 n × m n × m n×m 转化为 ( n − 3 ) × m (n - 3) × m (n3)×m

在这里插入图片描述

证明最优用三染色引理,暂时不会(

#include <iostream>

using namespace std;
int n, m;

int ans[3][3] = {
    {1, 2, 1},
    {2, 2, 2},
    {1, 2, 1}
};

int solve() {
    cin >> n >> m;
    if (n < m) swap(n, m);
    if (m == 1) return n + 1 >> 1;
    while (n > 4) n -= 3;
    while (m > 4) m -= 3;
    return ans[n - 2][m - 2];
}

int main() {
    int T; cin >> T;
    while (T --) cout << solve() << endl;
    return 0;
}

11-Circuit

原题详见 Acwing 334.观光之旅->,但原题不需要计算最小路径的数量,有一些差异

题意 无重边无自环有向图有 n n n 个点 m m m 条边,计算最短回路的长度和数量

Tag Floyd

题解 c n t [ i ] [ j ] cnt[i][j] cnt[i][j] 表示 i i i j j j 的最短边数量

外层枚举 k k k d [ i ] [ j ] d[i][j] d[i][j] 表示除了 i i i j j j 其余点下标小于等于 k k k i i i j j j 的最短距离,再更新

对于为什么要把更新 c o u n t count count 的循环放在枚举 k k k 的循环里面,作以下解释:

首先复习以下 f l o y d floyd floyd 算法,起初是三维的,但是可以写作二维
f l o y d { 状态表示 { f [ k ] [ i ] [ j ] : 表示只经过前 k 个点 ( 不考虑 i , j 两个端点 ) 从 i 到 j 的距离 属性 : m i n 状态计算: f [ k ] [ i ] [ j ] = { f [ k − 1 ] [ i ] [ j ] : 不经过 k 从 i 到 j f [ k − 1 ] [ i ] [ k ] + f [ k − 1 ] [ k ] [ j ] : 经过 k 从 i 到 j floyd\begin{cases}状态表示\begin{cases}f[k][i][j]:表示只经过前k个点(不考虑i,j两个端点)从i到j的距离\\属性:min\end{cases}\\状态计算:f[k][i][j]=\begin{cases}f[k-1][i][j]:不经过k从i到j\\f[k-1][i][k]+f[k-1][k][j]:经过k从i到j\end{cases}\end{cases} floyd 状态表示{f[k][i][j]:表示只经过前k个点(不考虑i,j两个端点)ij的距离属性:min状态计算:f[k][i][j]={f[k1][i][j]:不经过kijf[k1][i][k]+f[k1][k][j]:经过kij

每次更新的 c n t cnt cnt 是路径 k → i → k k→i→k kik 的长度,首先看 w [ k ] [ i ] w[k][i] w[k][i] 是否存在,然后枚举 i < k i<k i<k,是为了防止重复计算路径 i → k → i i→k→i iki 的长度(与路径 k → i → k k→i→k kik 相同)。上述只考虑了路径长度为 2 2 2,但是当路径长度多起来,例如 k → i → j → k k→i→j→k kijk,有 i < k < j i<k<j i<k<j 时,会发现仍然会重复计算,所以不能只保证 k k k 确定时枚举 i < k i<k i<k,还要保证经过的点都要小于 k k k,不重不漏原则。

e . g . e.g. e.g. 如下图,枚举 k = 4 k=4 k=4 的时候,有路径 4 → 1 → 2 → 4 4→1→2→4 4124 4 → 3 → 4 4→3→4 434 4 → 1 → 5 → 4 4→1→5→4 4154;但是枚举 k = 5 k=5 k=5 时也发现会美剧到路径 5 → 4 → 1 → 5 5→4→1→5 5415,可能会出现重复计算

在这里插入图片描述

#include <iostream>

using namespace std;
typedef long long ll;
const int mod = 998244353, N = 502;
const ll inf = 1e12;
int n, m, w[N][N], cnt[N][N];
ll d[N][N];

void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++)
            w[i][j] = 0, d[i][j] = inf;
    for (int i = 1; i <= n; i ++) d[i][i] = 0;
    while (m --) {
        int a, b, c; cin >> a >> b >> c;
        w[a][b] = d[a][b] = c;
        cnt[a][b] = 1;
    }
    ll minn = inf, count = 0;
    for (int k = 1; k <= n; k ++) {
        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= n; j ++) {
                if (d[i][j] > d[i][k] + d[k][j]) {
                    d[i][j] = d[i][k] + d[k][j];
                    cnt[i][j] = 1ll * cnt[i][k] * cnt[k][j] % mod;
                } else if (d[i][j] == d[i][k] + d[k][j]) {
                    cnt[i][j] = (cnt[i][j] + 1ll * cnt[i][k] * cnt[k][j]) % mod;
                }
            }
        }
        for (int i = 1; i < k; i ++) {
            if (w[k][i]) {
                if (w[k][i] + d[i][k] < minn) {
                    minn = w[k][i] + d[i][k];
                    count = cnt[i][k];
                } else if (w[k][i] + d[i][k] == minn) {
                    count = (count + cnt[i][k]) % mod;
                }
            }
        }
    }
    if (minn == inf) minn = count = -1;
    cout << minn << ' ' << count << endl;
}

int main() {
    cin.tie(nullptr) -> sync_with_stdio(false);
    int T; cin >> T;
    while (T --) solve();
    return 0;
}

12-a-b Problem

题意 一共 n 个石头,AB 捡石头,对应给每个人的价值不一样,要使得 V a l u e A − V a l u e B Value_A - Value_B ValueAValueB 尽可能大

Tag 贪心

题解 对于单个石头,对 A 价值为 V A V_A VA,对 B 价值为 V B V_B VB,则若 A 拾取,则对 A 的贡献度为 V A + V B V_A+V_B VA+VB,故对两者相加逆序排序即可

#include <iostream>
#include <vector>
#include <algorithm>
#define aa first
#define bb second

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 2;
int n, a[N], b[N];
vector<pii> c(N);

ll solve() {
    cin >> n;
    for (int i = 0; i < n; i ++) {
        cin >> a[i] >> b[i];
        c[i] = {a[i] + b[i], i};
    }
    sort(c.begin(), c.begin() + n);
    ll res = 0;
    for (int i = n - 1, idx = 1; i >= 0; i --, idx ++) {
        if (idx & 1) res += a[c[i].bb];
        else res -= b[c[i].bb];
    }
    return res;
}

int main() {
    int T; cin >> T;
    while (T --) cout << solve() << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JorbanS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值