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} Cn−12+n−12×Cn−12+n−1
最大值除了
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 m≤n,再分类讨论:(此题解参考官方题解)
- 当 m = 1 m = 1 m=1 时,答案显然为 ⌈ m / 2 ⌉ ⌈m/2⌉ ⌈m/2⌉
- 当 n ≥ 5 n ≥ 5 n≥5 时,可证明: n × m n × m n×m 等价于 ( n − 2 ) × m (n-2) × m (n−2)×m
可都转换成 2 ≤ n ≤ m ≤ 4 2 ≤ n ≤ m ≤ 4 2≤n≤m≤4 的情形,再简单手玩得到答案
d e f def def 基本操作, n ≥ 3 n ≥ 3 n≥3 时,一次消去三格,可以将 n × m n × m n×m 转化为 ( n − 3 ) × m (n - 3) × m (n−3)×m
当 n = 2 n = 2 n=2 时,可以将 n × m n × m n×m 转化为 ( n − 3 ) × m (n - 3) × m (n−3)×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两个端点)从i到j的距离属性:min状态计算:f[k][i][j]={f[k−1][i][j]:不经过k从i到jf[k−1][i][k]+f[k−1][k][j]:经过k从i到j
每次更新的 c n t cnt cnt 是路径 k → i → k k→i→k k→i→k 的长度,首先看 w [ k ] [ i ] w[k][i] w[k][i] 是否存在,然后枚举 i < k i<k i<k,是为了防止重复计算路径 i → k → i i→k→i i→k→i 的长度(与路径 k → i → k k→i→k k→i→k 相同)。上述只考虑了路径长度为 2 2 2,但是当路径长度多起来,例如 k → i → j → k k→i→j→k k→i→j→k,有 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 4→1→2→4, 4 → 3 → 4 4→3→4 4→3→4, 4 → 1 → 5 → 4 4→1→5→4 4→1→5→4;但是枚举 k = 5 k=5 k=5 时也发现会美剧到路径 5 → 4 → 1 → 5 5→4→1→5 5→4→1→5,可能会出现重复计算
#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
个石头,A
和 B
捡石头,对应给每个人的价值不一样,要使得
V
a
l
u
e
A
−
V
a
l
u
e
B
Value_A - Value_B
ValueA−ValueB 尽可能大
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;
}