2024 (ICPC) Jiangxi Provincial 省赛

前言

和队友 vp 7t,赛后补了几题。

A. Maliang Learning Painting

思路

输出 a + b + c

代码

cin>>n>>m>>k;
cout<<n+m+k<<endl;

C. Liar

思路

队友写得

代码

void solve() {
int n,k;
cin>>n>>k;
int ans=0;
vector<int>a(n);
for (int i = 0; i <n ; ++i) {
cin>>a[i];
}
sort(a.begin(), a.end());
for (int i = 0; i <n ; ++i) {
ans+=a[i];
}
if(ans==k)
cout<<n<<endl;
else{
cout<<n-1<<endl;
}
}

D. Magic LCM

思路

数学、质因数分解

考虑 \(a,b\) 互质,则其中一方会变为 \(1\),另一方变为 \(ab\);若 \(a=kx,b=ky\) ,则有一方变为 \(k\),另一方变为 \(kxy\) ,因为 \(xy>x+y\),所以我们应该把质因子尽量地合在一起,且要独立考虑每个质因子的幂次,即将各质因子按幂次从大到小地尽量合,最后就是要么不互质,要么就是 1,所以可以考虑用筛法将质数筛出来,为了降低复杂度,只筛 1e3 以内的,一个小于 1e6 的数不可能产生幂次大于 1 且大于 1e3 的质因子,因此碰到大质数我们直接乘到对应的答案中,小质数需要按照幂次排序再相乘。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

constexpr i64 mod = 998244353;

i64 ksm(i64 x, i64 y) {
i64 res = 1;
while (y) {
if (y & 1) res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
} //欧拉函数,质数
vector<int> euler_range(int n) {
vector<int> phi(n + 1), prime;
vector<bool> is_prime(n + 1, true);
is_prime[1] = 0, phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (is_prime[i]) prime.push_back(i), phi[i] = i - 1;
for (int j = 0; j < (int)prime.size() && i * prime[j] <= n; j++) {
is_prime[i * prime[j]] = 0;
if (i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j] - 1);
else {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
}
}
return prime;
} constexpr int N = 1e3;
auto pr = euler_range(N); void solve() { int n;
cin >> n; vector<vector<int>> mp(pr.back() + 1);
vector<i64> res(n + 1, 1);
map<int, int> mp2; int noone = 0, a, cnt = 0;
for (int i = 1; i <= n; i ++) {
cin >> a;
for (auto &j : pr) {
if (a % j == 0) {
cnt = 0;
while (a % j == 0) {
a /= j;
cnt ++;
}
mp[j].emplace_back(cnt);
noone = max(noone, (int)mp[j].size());
}
}
if (a > 1) {
(res[++mp2[a]] *= a) %= mod;
noone = max(noone, mp2[a]);
}
} i64 ans = n - noone; for (auto v : pr) {
if (mp[v].size()) {
sort(mp[v].begin(), mp[v].end());
}
} for (int i = 1; i <= noone; i ++) {
for (auto &v : pr) {
if (mp[v].size()) {
auto mi = mp[v].back();
mp[v].pop_back();
res[i] = res[i] * ksm(v, mi) % mod;
}
} ans = (ans + res[i]) % mod;
} cout << ans << '\n'; } int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); int t;
cin >> t;
while (t--) {
solve();
} return 0;
}

E. Magic Subsequence

思路

双向 dfs,三进制枚举(?

注意到一个长度为 n 的正整数序列的子序列的和最多只有 \(2^n\) 种,那么取 \(n = 30\),有 \(2^n > M × n\) 存在,因此本题只需要通过任意 30 个数进行判断。(实际上存在更紧凑的界

对于子集的选取,若有解,一定存在一个子集间互相不交的解,那么 30 个数可以分为三类,属于集合 A 的数,属于集合 B 的数,不在集合中的数。所以可以采用双向 dfs 搜索,不过我这里采取的是三进制枚举,带了个 10 左右的常数,跑满 \(3^{15}\) 会超时,所以只跑了 \(3^{13}\) 和 \(3^{14}\) ,就像上面说的跑不满,选取28会TLE,26会WA,很神奇(,实际写的话还是更推荐 dfs,最后合并的时候可以用归并,也可以哈希一下前半段的差,后半段搜到符合条件的解直接输出即可。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

    int n;
cin >> n; vector<int> a(n + 1);
for (int i = 1; i <= n; i ++)
cin >> a[i]; n = min(n, 27);
int mid = n >> 1; int end1 = 1;
for (int i = 1; i <= mid ; i ++)
end1 *= 3;
vector<pair<int, int>> pre, suf; for (int i = 1; i < end1; i ++) {
int sum1 = 0, sum2 = 0;
int x = i;
for (int j = 1; j <= mid; j ++) {
if (x % 3 == 1) sum1 += a[j];
if (x % 3 == 2) sum2 += a[j];
x /= 3;
}
pre.emplace_back(sum1 - sum2, i);
} int end2 = 1;
for (int i = 1; i <= n - mid; i++)
end2 *= 3;
for (int i = 1; i < end2; i ++) {
int sum1 = 0, sum2 = 0;
int x = i;
for (int j = 1 + mid; j <= n; j ++) {
if (x % 3 == 1) sum2 += a[j];
if (x % 3 == 2) sum1 += a[j];
x /= 3;
}
suf.emplace_back(sum2 - sum1, i);
} auto print = [ &mid, &n](int pre, int suf)->void{
vector<int> A, B;
for (int i = 1; i <= mid; i ++) {
if (pre % 3 == 1) A.emplace_back(i);
if (pre % 3 == 2) B.emplace_back(i);
pre /= 3;
}
for (int i = 1 + mid; i <= n; i ++) {
if (suf % 3 == 1) A.emplace_back(i);
if (suf % 3 == 2) B.emplace_back(i);
suf /= 3;
}
cout << A.size() << ' ';
for (auto i : A) {
cout << i << " \n"[i == A.back()];
}
cout << B.size() << ' ';
for (auto i : B) {
cout << i << " \n"[i == B.back()];
}
}; sort(pre.begin(), pre.end());
sort(suf.begin(), suf.end()); int l = 0, r = suf.size() - 1;
while (l < pre.size() && r >= 0) {
auto [pv, pst] = pre[l];
auto [sv, sst] = suf[r];
if (pv + sv == 0) {
print(pst, sst);
return;
}
if (pv < -sv) l ++;
else r --;
} cout << "-1\n"; } int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); int t;
cin >> t;
while (t--) {
solve();
} return 0;
}

G. Multiples of 5

思路

队友写得,放个官方题解吧

考虑到对每个位的数独立考虑对 5 取模的余数,假设第 i 位的数为 x,它们的贡献为 \((11^i−1 × x) \bmod 5\)。

对连续长度的同一个数的贡献可以等比数列求和,根据实现细节的不同可能需要使用逆元。

注意到 \(11^i\) 的末位均为 1,因此其对 5 取模的结果均为 1,那么所有数位的贡献都是相等的,直接考虑有多少个当前数计算即可。

代码

void solve() {
cin>>n;
char x;
int s=0;
for(int i=1;i<=n;i++){
cin>>a[i];
cin>>x;
int t;
if(x=='A'){
t=x-'A'+10;
}else{
t=x-'0';
}
s+=(t*a[i]%5);
}
if(s%5){
cout<<"No\n";
}else{
cout<<"Yes\n";
}
}

H.Convolution

思路

队友写得,放个官解吧

考虑卷积核 \(K\) 中的每个元素产生贡献。\(K\) 中的每个元素都对应矩阵 \(I\) 中的一个子矩阵。

例如 \(K_{i,j}\) 产生的贡献是 \(K_{i,j}×\) 矩阵 $I_{i,j∼n−k+i,m−l+j} $的子矩阵之和,其中的 \(k, l\) 是卷积核 \(K\) 的长宽。

所以当子矩阵和为负数时,\(K_{i,j}\) 取 −1;子矩阵和为正数时,\(K_{i,j}\) 取 1。快速求出某个子矩阵和可以用二维前缀和来实现。

代码

void solve() {
cin>>n>>m>>k>>l;
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++){
b[i][j]=b[i-1][j]+a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i<=n-k+1)c[i][j]=c[i][j-1]+b[i][j];
else{
c[i][j]=c[i][j-1]+b[i][j]-b[i-(n-k+1)][j];
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(j<=m-l+1)d[i][j]=c[i][j];
else{
d[i][j]=c[i][j]-c[i][j-(m-l+1)];
}
}
}
int ans=0;
for(int i=0;i<k;i++){
for(int j=0;j<l;j++){
ans+=abs(d[i+n-k+1][j+m-l+1]);
}
}
cout<<ans<<endl;
}

I. Neuvillette Circling

思路

计算几何

实际上所有的最小覆盖圆,都是由这 n 个点中的两个点(作为直径时)或者三个点确定。所以只需要 \(n^2 + n^3\) 分别枚举所有圆,并且再分别计算每个圆覆盖了多少点和它的半径。这样就可以维护所有 k 点覆盖圆中最小的半径。总复杂度为 \(O(n^4)\)。

\(n^2\) 两点距离作为直径,中心作为圆心,\(n^3\) 三点求外切圆。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

constexpr double eps = 1e-8;   // 根据题目精度要求进行修改
constexpr double PI = acos(-1.0); // pai, 3.1415916.... int sgn(double x) { // 进行判断, 提高精度
if (fabs(x) < eps) return 0; // x == 0, 精度范围内的近似相等
return x > 0 ? 1 : -1; // 返回正负
} // Need: sgn() typedef struct Point {
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) {} // 构造函数, 初始值为 0 // 重载运算符
// 点 - 点 = 向量(向量AB = B - A)
Point operator- (const Point &B) const { return Point(x - B.x, y - B.y); } // 点 + 点 = 点, 点 + 向量 = 向量, 向量 + 向量 = 向量
Point operator+ (const Point &B) const { return Point(x + B.x, y + B.y); } // 向量 × 向量 (叉积)
double operator^ (const Point &B) const { return x * B.y - y * B.x; } // 向量 · 向量 (点积)
double operator* (const Point &B) const { return x * B.x + y * B.y; } // 点 * 数 = 点, 向量 * 数 = 向量
Point operator* (const double &B) const { return Point(x * B, y * B); } // 点 / 数 = 点, 向量 / 数 = 向量
Point operator/ (const double &B) const { return Point(x / B, y / B); } // 判断大小, 一般用于排序
bool operator< (const Point &B) const { return x < B.x || (x == B.x && y < B.y); } // 判断相等, 点 == 点, 向量 == 向量, 一般用于判断和去重
bool operator== (const Point &B) const { return sgn(x - B.x) == 0 && sgn(y - B.y) == 0; } // 判断不相等, 点 != 点, 向量 != 向量
bool operator!= (const Point &B) const { return sgn(x - B.x) || sgn(y - B.y); }
} Vector; // Need: (-, *) double dist(Point a, Point b) { return sqrt((a - b) * (a - b)); } // Need: Point() struct Circle {
Point o;
double r;
Circle(Point _o = Point(), double _r = 0) : o(_o), r(_r) {} // 圆的面积
double Circle_S() { return PI * r * r; }
// 圆的周长
double circle_C() { return 2 * PI * r; }
}; // Need: sgn(), dist() // 点在圆上, 返回 0
// 点在圆外, 返回 -1
// 点在圆内, 返回 1 int Point_with_circle(Point p, Circle c) {
double d = dist(p, c.o);
if (sgn(d - c.r) == 0) return 0;
if (sgn(d - c.r) > 0) return -1;
return 1;
} // Need: dist() Circle get_circumcircle(Point A, Point B, Point C) {
double Bx = B.x - A.x, By = B.y - A.y;
double Cx = C.x - A.x, Cy = C.y - A.y;
double D = 2 * (Bx * Cy - By * Cx); double x = (Cy * (Bx * Bx + By * By) - By * (Cx * Cx + Cy * Cy)) / D + A.x;
double y = (Bx * (Cx * Cx + Cy * Cy) - Cx * (Bx * Bx + By * By)) / D + A.y;
Point P(x, y);
return Circle(P, dist(A, P));
} int main() {
// ios::sync_with_stdio(false);
// cin.tie(nullptr); int n;
scanf("%d", &n); vector<Vector> p;
for (int i = 1; i <= n; i ++) {
double x, y;
scanf("%lf%lf", &x, &y);
p.emplace_back(x, y);
} vector<double> ans(n * (n - 1) / 2 + 1, 1e15);
for (int i = 0; i < n; i ++) {
for (int j = i + 1; j < n; j ++) {
Point mid((p[i].x + p[j].x) * 0.5, (p[i].y + p[j].y) * 0.5);
double R = dist(mid, p[i]);
Circle O(mid, R); int num = 0;
for (int k = 0; k < n; k ++) {
if (Point_with_circle(p[k], O) != -1)num++;
}
int op = num * (num - 1) / 2;
for (int idx = 1; idx <= op; idx ++)
ans[idx] = min(ans[idx], R);
}
} for (int i = 0; i < n; i ++) {
for (int j = i + 1; j < n; j ++) {
for (int k = j + 1; k < n; k ++) {
Circle O = get_circumcircle(p[i], p[j], p[k]);
int num = 0;
for (int l = 0; l < n; l ++) {
if (Point_with_circle(p[l], O) != -1)
num++;
}
int op = num * (num - 1) / 2;
for (int idx = 1; idx <= op; idx ++)
ans[idx] = min(ans[idx], O.r);
}
}
} for (int i = 1; i <= n * (n - 1) / 2; i ++) {
printf("%.10lf\n", ans[i]);
} return 0;
}

J.Magic Mahjong

思路

字符串模拟。

需要注意国士无双是 13 种幺九牌各一张再加上其中任意一张,且牌之间是无序的。

需要注意七对子不能有相同的对子。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

set<string> Thirteen, seven;

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); string t = "1p9p1s9s1m9m1z2z3z4z5z6z7z"; for (int i = 0; i < t.size(); i += 2) {
Thirteen.insert(t.substr(i, 2));
} int n;
cin >> n; while (n --) { string s;
cin >> s; bool f = 1;
set<string> k;
for (int i = 0; i < s.size(); i += 2) {
string p = s.substr(i, 2);
k.insert(p);
if (!Thirteen.count(p)) f = 0;
} if (f && k.size() == 13) {
cout << "Thirteen Orphans\n";
} else if (k.size() == 7) {
cout << "7 Pairs\n";
} else {
cout << "Otherwise\n";
} } return 0;
}

K. Magic Tree

思路

诈骗题。

注意到换行后树实际上截断了,原来一侧的是一条没有选择的链。可以基于这个进行简单的动态规划。

继续观察可以发现,当上次 dfs 时进行了换行后,下一次操作只有一种情况(另一侧延伸是死路),而如果当前没有换行,下一次操作可以选择继续延伸或者不换行。

因为列增加即会导致两种可能,因此方案数为 \(2^{n−1}\)

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

constexpr i64 mod = 998244353;

i64 ksm(i64 x, i64 y) {
i64 res = 1;
while (y) {
if (y & 1) res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
} int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); i64 m;
cin >> m; cout << ksm(2, m - 1) % mod << '\n'; return 0;
}

L. Campus

思路

最短路

时间段最多只有 \(2k+1\) 个,也就是 15 个互不相交的区间和没有大门开的区间,所以只要对这 k 个大门都做一遍 dijkstra ,然后判断哪些时间段有哪些大门开着即可。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

struct DIJ {
using i64 = long long;
using PII = pair<i64, i64>;
vector<i64> dis;
vector<vector<PII>> G; DIJ() {}
DIJ(int n) {
dis.assign(n + 1, 1e18);
G.resize(n + 1);
} void add(int u, int v, int w) {
G[u].emplace_back(v, w);
} void dijkstra(int s) {
priority_queue<PII> que;
dis[s] = 0;
que.push({0, s});
while (!que.empty()) {
auto p = que.top();
que.pop();
int u = p.second;
if (dis[u] < p.first) continue;
for (auto [v, w] : G[u]) {
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
que.push({ -dis[v], v});
}
}
}
}
}; int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); int n, m, k, T;
cin >> n >> m >> k >> T; vector<int> a(n + 1);
for (int i = 1; i <= n; i ++)
cin >> a[i]; vector<DIJ> dij(k, n); vector<array<int, 3>> door(k);
for (auto &[p, l, r] : door) {
cin >> p >> l >> r;
} for (int i = 0; i < m; i ++) {
int u, v, w;
cin >> u >> v >> w;
for (int j = 0; j < k; j ++) {
dij[j].add(u, v, w);
dij[j].add(v, u, w);
}
} for (int i = 0; i < k; i ++) {
dij[i].dijkstra(door[i][0]);
} vector<i64> dis;
auto res = [&](int x)->i64{
if (!x) return -1;
vector<i64>(n + 1, 1e18).swap(dis);
for (int i = 0; i < k; i ++) {
if (x >> i & 1) {
for (int j = 1; j <= n; j ++) {
dis[j] = min(dis[j], dij[i].dis[j]);
}
}
} i64 res = 0;
for (int i = 1; i <= n; i ++)
res += a[i] * dis[i];
return res;
}; unordered_map<int, i64> ans; for (int t = 1; t <= T; t ++) {
int mask = 0;
for (int i = 0; i < k; i ++) {
auto &[p, l, r] = door[i];
if (l <= t && t <= r)
mask |= 1 << i;
} if (!ans.count(mask)) {
ans[mask] = res(mask);
} cout << ans[mask] << '\n';
} return 0;
}

2024 (ICPC) Jiangxi Provincial 省赛的更多相关文章

  1. HDU 5127.Dogs' Candies-STL(vector)神奇的题,set过不了 (2014ACM/ICPC亚洲区广州站-重现赛(感谢华工和北大))

    周六周末组队训练赛. Dogs' Candies Time Limit: 30000/30000 MS (Java/Others)    Memory Limit: 512000/512000 K ( ...

  2. HDU 5135.Little Zu Chongzhi's Triangles-字符串 (2014ACM/ICPC亚洲区广州站-重现赛)

    Little Zu Chongzhi's Triangles Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 512000/512000 ...

  3. HDU 5131.Song Jiang's rank list (2014ACM/ICPC亚洲区广州站-重现赛)

    Song Jiang's rank list Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 512000/512000 K (Java ...

  4. ICPC 2019 徐州网络赛

    ICPC 2019 徐州网络赛 比赛时间:2019.9.7 比赛链接:The Preliminary Contest for ICPC Asia Xuzhou 2019 赛后的经验总结 // 比赛完才 ...

  5. ICPC 2018 亚洲横滨赛 C Emergency Evacuation(暴力,贪心)

    ICPC 2018 亚洲横滨赛 C Emergency Evacuation 题目大意 你一个车厢和一些人,这些人都坐在座位上,求这些人全部出去的时间最小值 Solution 题目咋说就咋做 直接模拟 ...

  6. ICPC 2018 焦作区域赛

    // 2019.10.7 练习赛 // 赛题来源:2018 ICPC 焦作区域赛 // CF链接:http://codeforces.com/gym/102028 A Xu Xiake in Hena ...

  7. HDU 5875 Function -2016 ICPC 大连赛区网络赛

    题目链接 网络赛的水实在太深,这场居然没出线zzz,差了一点点,看到这道题的的时候就剩半个小时了.上面是官方的题意题解,打完了才知道暴力就可以过,暴力我们当时是想出来了的,如果稍稍再优化一下估计就过了 ...

  8. 2014 ACM/ICPC 鞍山赛区现场赛 D&amp;I 解题报告

    鞍山现场赛结束了呢-- 我们出的是D+E+I三道题-- 吾辈AC掉的是D和I两道,趁着还记得.先在这里写一写我写的两道水题D&I的解题报告吧^_^. D题的意思呢是说星云内有一堆排成一条直线的 ...

  9. 2014 ACM/ICPC 鞍山赛区网络赛(清华命题)

    为迎接10月17号清华命题的鞍山现场赛 杭电上的题目 Biconnected(hdu4997)     状态压缩DP Rotate(hdu4998)    相对任一点的旋转 Overt(hdu4999 ...

  10. HDU-5532//2015ACM/ICPC亚洲区长春站-重现赛-F - Almost Sorted Array/,哈哈,水一把区域赛的题~~

    F - Almost Sorted Array Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & ...

随机推荐

  1. git客户端安装和使用

    需要安装三个软件 1.git客户端 点击下载 下载完成后一只next就行了. 2.git右键属性的扩展程序 点击下载 下载完成后一只next就行了 3.git中文包 点击下载 下载完成后一只next就 ...

  2. FFmpeg如何将一个gif嵌入视频指定位置并指定显示时间

    背景 很简单的需求:我需要将一个gif嵌入到视频里面的指定位置,并要指定时间播放: 环境 windows11 64位专业版 ffmpeg version 2022-04-07-git-607ecc27 ...

  3. Pycharm或cmd在Terminal中运行tensorboard、pip等python包

    这个主要是添加python包的路径到环境变量里 因为装了anaconda,所以我们要找的是对应虚拟环境里的包路径,一般是放在anaconda安装路径下的anaconda3\envs\环境名\Scrip ...

  4. 2023/3/21 组会:ChatGPT 对数据增强的影响及 ChatGPT 的鲁棒性,Dense 和 Document 检索方法

    前两个也许跟上了,后两个完全没跟上,以后再详细读读吧qwq 反正组会跟不上才是正常现象. AugGPT: Leveraging ChatGPT for Text Data Augmentation 摘 ...

  5. 在高通lk中添加自定义源文件

    在高通lk中添加自定义源文件 背景 在lk开发中,需要添加一个自定义功能,但是又不希望代码污染无关的文件(把无关代码添加到某个源文件中是一种罪). 以添加一个aw9523b.c的驱动为例,在aboot ...

  6. HTTP协议 学习:2-基于libcurl的开发

    HTTP协议 学习:2-基于libcurl的开发 背景 上一讲我们介绍了HTTP报文的一些内容,这一讲我们基于http有关的开源库,进行HTTP通信.最后再完成一个简单的下载小程序. ref : ht ...

  7. Coap 协议学习:具体协议介绍具体

    协议框架 CoAP默认运行在UDP上,但它也支持运行在SMS,TCP等数据传输层上.本文主要是基于UDP上的CoAP协议介绍 1.消息模型 Messages COAP协议通信是通过在UDP上传输消息类 ...

  8. 《HelloGitHub》第 99 期

    兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. github.com/521xueweihan/HelloG ...

  9. AT_abc215F 题解

    考虑二分答案. 假设当前二分的答案为 \(k\),那么对于每个点,距离大于等于 \(k\) 的点构成了平面上 \(4\) 个子平面. 那么只需查询子平面中是否存在点即可,类似于窗口的星星,把问题转换成 ...

  10. MinIO使用记录

    探索MinIO:高性能.分布式对象存储解决方案 注:本文除代码外多数为AI生成 最近因为有项目需要换成Amazon S3的云存储,所以把之前做过的minio部分做一个记录,后面也会把基于这版改造的S3 ...