20240920 随机训练
GYM 105348 A
题目描述
有 \(N\) 个团队要两两进行一次比赛,每一场比赛恰好一方胜,一方败。你可以决定每场比赛的胜负。
接着我们要对这些团队进行排名。胜场多的排在前面,胜场一样的积分率高的排在前面。积分率也由你控制。
你要让你的团队进入前四名,求你的团队的最小胜场数。
思路
我们想让自己赢得尽可能少,也就是让别人尽可能多,所以让前三名分别赢 \(N-1,N-2,N-3\) 场,然后让其余人赢得场数尽可能平均即可。
时空复杂度均为 \(O(1)\)。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int t, n;
void Solve() {
cin >> n;
ll x = 1ll * n * (n - 1) / 2 - (n - 1) - (n - 2) - (n - 3);
cout << x / (n - 3) + (x % (n - 3) > 0) << "\n";
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
for(cin >> t; t--; Solve()) {
}
return 0;
}
GYM 105348 F
题目描述
给定一个长度为 \(N\) 的排列 \(A\),我们定义一个长度为 \(k\) 的数组 \(a\) 的价值如下:
- 构造一个长度为 \(k\) 的排列 \(b\),使得元素在 \(b\) 中的相对大小与 \(a\) 相同。
- \(a\) 的价值为 \(b_k\)。
求 \(A\) 的所有非空字串的价值之和。
思路
我们考虑每个数对答案的贡献,一个数 \(A_i\) 会对所有 \(j\ge i,A_j\ge A_i\) 的数造成 \(i\) 的贡献,因为只要左端点 \(\le i\),那么就会使答案 \(+1\)。使用树状数组维护即可。
空间复杂度 \(O(N)\),时间复杂度 \(O(N\log N)\)。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 1000001;
int t, n;
ll ans, tr[MAXN];
void update(int p, int x) {
for(; p <= n; tr[p] += x, p += (p & -p)) {
}
}
ll Getsum(int p) {
ll sum = 0;
for(; p; sum += tr[p], p -= (p & -p)) {
}
return sum;
}
void Solve() {
cin >> n;
ans = 0;
for(int i = 1; i <= n; ++i) {
tr[i] = 0;
}
for(int i = 1, x; i <= n; ++i) {
cin >> x;
update(x, i);
ans += Getsum(x);
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
for(cin >> t; t--; Solve()) {
}
return 0;
}
GYM 105345 I
题目描述
有 \(N\) 个房子,第 \(i\) 个房子将在时刻 \(A_i\) 关闭,你一开始在第 \(k\) 个房子,每一秒钟你可以走到左/右边的房子。你要在一个房子关闭前到达。如果你在房子关闭的时刻刚好到达也不行。
如果你不能造访所有的房子,输出 \(-1\),否则输出最少的消耗时间。
思路
区间 dp 即可。
时空复杂度均为 \(O(N^2)\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2001, INF = int(1e9);
int n, k, a[MAXN], dp[MAXN][MAXN][2];
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> k;
for(int i = 1; i <= n; ++i) {
cin >> a[i];
}
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
dp[i][j][0] = dp[i][j][1] = INF;
}
}
dp[k][k][0] = dp[k][k][1] = 0;
for(int len = 1; len < n; ++len) {
for(int l = 1, r = len; r <= n; ++l, ++r) {
if(l > 1) {
dp[l - 1][r][0] = min({dp[l - 1][r][0], (dp[l][r][0] + 1 < a[l - 1] ? dp[l][r][0] + 1 : INF), (dp[l][r][1] + r - l + 1 < a[l - 1] ? dp[l][r][1] + r - l + 1 : INF)});
}
if(r < n) {
dp[l][r + 1][1] = min({dp[l][r + 1][1], (dp[l][r][1] + 1 < a[r + 1] ? dp[l][r][1] + 1 : INF), (dp[l][r][0] + r - l + 1 < a[r + 1] ? dp[l][r][0] + r - l + 1 : INF)});
}
}
}
cout << (min(dp[1][n][0], dp[1][n][1]) == INF ? -1 : min(dp[1][n][0], dp[1][n][1]));
return 0;
}
GYM 105345 J
题目描述
有 \(N\) 张牌,第 \(i\) 张牌上的数字为 \(d_i\)。有 \(Q\) 次询问:
- 将第 \(i\) 张牌设为 \(x\)。
- 求有多少种方案从 \(d_l,d_{l+1},\dots,d_r\) 中选出一些数,使得其乘积 \(\bmod 13 =5\)。
思路
我们用 \(v_i\) 表示使得乘积为 \(i\) 的方案数,并使用线段树维护。
很明显有以下转移:\(v_{2,ij\bmod 13} \leftarrow v_{0,i}\cdot v_{1,j}\)。
空间复杂度 \(O(NV)\),时间复杂度 \(O(QV\log N)\)。其中 \(V=13\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10001, MOD = int(1e9) + 7;
struct INFO {
int d[13];
INFO operator+(const INFO &x) {
INFO ret;
for(int i = 0; i < 13; ++i) {
ret.d[i] = (d[i] + x.d[i]) % MOD;
}
for(int i = 0; i < 13; ++i) {
for(int j = 0; j < 13; ++j) {
ret.d[i * j % 13] = (ret.d[i * j % 13] + 1ll * d[i] * x.d[j] % MOD) % MOD;
}
}
return ret;
}
INFO operator+=(const INFO &x) {
return *this = *this + x;
}
};
struct Segment_Tree {
int l[MAXN << 2], r[MAXN << 2], a[MAXN];
INFO v[MAXN << 2];
void build(int u, int s, int t) {
l[u] = s, r[u] = t;
if(s == t) {
v[u].d[a[s]] = 1;
return;
}
int mid = (s + t) >> 1;
build(u << 1, s, mid), build((u << 1) | 1, mid + 1, t);
v[u] = v[u << 1] + v[(u << 1) | 1];
}
void update(int u, int p, int x) {
if(l[u] == r[u]) {
for(int i = 0; i < 13; ++i) {
v[u].d[i] = (i == x);
}
return;
}
(p <= r[u << 1] ? update(u << 1, p, x) : update((u << 1) | 1, p, x));
v[u] = v[u << 1] + v[(u << 1) | 1];
}
INFO GetInfo(int u, int s, int t) {
if(l[u] >= s && r[u] <= t) {
return v[u];
}
INFO x;
for(int i = 0; i < 13; ++i) {
x.d[i] = 0;
}
if(s <= r[u << 1]) {
x += GetInfo(u << 1, s, t);
}
if(t >= l[(u << 1) | 1]) {
x += GetInfo((u << 1) | 1, s, t);
}
return x;
}
}tr;
int n, q;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> q;
for(int i = 1; i <= n; ++i) {
cin >> tr.a[i];
tr.a[i] %= 13;
}
tr.build(1, 1, n);
for(int i = 1, op, p, x, l, r; i <= q; ++i) {
cin >> op;
if(op == 1) {
cin >> p >> x;
tr.update(1, p, x % 13);
}else {
cin >> l >> r;
cout << tr.GetInfo(1, l, r).d[5] << "\n";
}
}
return 0;
}
GYM 105310 H
题目描述
给定一个 \(N\) 个结点,以 \(1\) 为根的树,每个结点都有一个美味度 \(v_i\)。有 \(Q\) 次查询,每次会令 \(v_x\leftarrow v_x +c\),并让你选择一个连通子图,使得该子图中深度最小的结点为 \(x\) 时的最大总美味度。
思路
首先考虑没有修改的情况,很明显有树上 dp:\(dp_u=v_u +\sum \limits_{v\in son_u} \max(0,dp_v)\)。这里 \(son_u\) 是 \(u\) 的儿子。
我们考虑转化一下这个式子:\(dp_{u}=sum_u-\sum \limits_{v\in S_u}dp_v\),这里 \(S_u\) 表示 \(u\) 子树内 \(dp_v<0\) 的 \(v\) 构成的集合,因为这些 \(dp\) 的贡献是负的,所以要减去。
这样我们就能更方便地维护这个 \(dp\)。由于这里 \(c\) 总是正的,所以每次只有可能令 \(dp_u<0\Rightarrow dp_u\ge 0\),也就是从减去变成了不减去,也就是加上,使用树状数组维护。而对每个点最多只有一次这样的变化,所以使用并查集找到祖先中第一个 \(dp<0\) 的点。
空间复杂度 \(O(N)\),时间复杂度 \(O((N+Q)\log N)\)。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 500001;
int n, q, dfn[MAXN], tot, sz[MAXN], f[MAXN], _f[MAXN], a[MAXN];
ll sum[MAXN], tr[MAXN];
vector<int> e[MAXN];
int lowbit(int x) {
return x & -x;
}
void update(int p, ll x) {
for(; p <= n; tr[p] += x, p += lowbit(p)) {
}
}
ll Getsum(int p) {
ll sum = 0;
for(; p; sum += tr[p], p -= lowbit(p)) {
}
return sum;
}
ll query(int l, int r) {
return Getsum(r) - Getsum(l - 1);
}
void dfs(int u, int fa) {
sum[u] = a[u], dfn[u] = ++tot, sz[u] = 1, _f[u] = fa;
for(int v : e[u]) {
if(v != fa) {
dfs(v, u);
sz[u] += sz[v];
sum[u] += sum[v];
}
}
ll x = sum[u] + query(dfn[u], dfn[u] + sz[u] - 1);
f[u] = _f[u];
if(x < 0 && _f[u]) {
f[u] = u;
update(dfn[_f[u]], -x);
}
}
int getfa(int u) {
return (f[u] == u ? u : f[u] = getfa(f[u]));
}
void modifly(int u, int x) {
update(dfn[u], x), u = getfa(u);
ll res = x;
for(; u && sum[u] + query(dfn[u], dfn[u] + sz[u] - 1) >= 0; u = getfa(u)) {
if(_f[u]) {
update(dfn[_f[u]], sum[u] + query(dfn[u], dfn[u] + sz[u] - 1) - res);
res += sum[u] + query(dfn[u], dfn[u] + sz[u] - 1) - res;
}
f[u] = _f[u];
}
if(_f[u]) {
update(dfn[_f[u]], -res);
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> q;
for(int i = 1; i <= n; ++i) {
cin >> a[i];
f[i] = i;
}
for(int i = 2, u; i <= n; ++i) {
cin >> u;
e[u].emplace_back(i);
}
dfs(1, 0);
for(int i = 1, u, w; i <= q; ++i) {
cin >> u >> w;
modifly(u, w);
cout << sum[u] + query(dfn[u], dfn[u] + sz[u] - 1) << "\n";
}
return 0;
}