2025湖南多校2
Dashboard - 2025 Hunan Multi-School Training Round 2 - Codeforces
D 排序后从中间向两边取。
I,J,B,H参考博客:https://www.cnblogs.com/lyrrr/p/18780739
给定长度为 \(n\) 的序列 \(a\),\(a_i\) 表示有 \(i\) 个中子的原子可以释放 \(a_i\) 的能量,一个中子数大于 \(n\) 的原子可以分裂成两个原子(中子数不为 \(0\)),问中子数为 \(k\) 的原子释放全部能量后,释放的能量的最小值。
容易猜到当 \(k\) 很大时,会一直分裂出 能量/中子数 最小的原子,直到小于某个值 \(m\)。
可以证明,\(m=n^2\) 时一定正确。
设 \(dp_i\) 表示中子数为 \(i\) 时的最小能量,预处理到 \(m\) 即可。
关于转移:
for(int j = n + 1; j <= m; ++j)
for(int i = 1; i <= n; ++i)
dp[j] = min(dp[j], dp[j - i] + a[i]);
for(int i = 1; i <= n; ++i)
for(int j = n + 1; j <= m; ++j)
dp[j] = min(dp[j], dp[j - i] + a[i]);
上面是对的,下面是错的,问题在于前 \(n\) 个状态是固定的,考虑 \(n=5\) 时,\(a_1=10000,a_2=1,a_3=1,a_4=10000,a_5=10000\),此时 \(dp_8\) 的结果应该为 \(3\)。(\(a_2+a_3+a_3\))
只能在 \(a_3\) 转移后,从 \(dp_6\) 转移到 \(dp_8\),但是不会在做 \(a_2\) 的转移。
若没有前 \(n\) 个状态固定的限制,则两者都对。
(太难察觉了,认了)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define int ll
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 105;
int n, q;
int a[N];
ll dp[N * N * N];
void solve()
{
n = read(), q = read();
int m = 1000000;
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 0; i <= m; ++i) dp[i] = 0x7fffffffffffffff;
for(int i = 1; i <= n; ++i) dp[i] = a[i];
for(int j = n + 1; j <= m; ++j)
for(int i = 1; i <= n; ++i)
dp[j] = min(dp[j], dp[j - i] + a[i]);
while(q--)
{
ll k = read();
if(k > m)
{
ll ans = 0x7fffffffffffffff;
for(int i = 1; i <= n; ++i)
{
ll t = (k - m + i - 1) / i;
ans = min(ans, t * a[i] + dp[k - t * i]);
}
printf("%lld\n", ans);
}else
{
printf("%lld\n", dp[k]);
}
}
}
signed main()
{
int T = 1;
while(T--) solve();
return 0;
}
给定一棵树,一些节点被染色成红色,一个节点的代价为它与距离它最近的红色祖先节点的距离。
多次询问,每次给定一个集合,至多可以在再将树上的一个节点染成红色,求集合中的点的代价的最小值。
二分答案,此时集合中的一些点不满足条件,将这些点的 \(LCA\) 染成红色,再判断是否符合答案。
复杂度为 \(O((\sum{k})\log k\log \sum w)\)。
可以不使用二分,因为每个点的代价是固定的,将这些点按照代价从大到小排序后,不满足条件的点一定是一个前缀。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define int ll
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const ll lim = 1e14;
const int N = 1e5 + 5;
int n, m, q;
int red[N];
vector< pair<int, int> > to[N];
int top[N], f[N], son[N], Size[N], depth[N], flag[N];
ll dis[N];
void dfs1(int k, int fa, int Flag)
{
depth[k] = depth[fa] + 1;
if(red[k]) Flag = k;
flag[k] = Flag;
Size[k] = 1;
son[k] = 0;
f[k] = fa;
for(auto [v, w] : to[k])
{
if(v == fa) continue;
dis[v] = dis[k] + w;
dfs1(v, k, Flag);
Size[k] += Size[v];
if(Size[v] > Size[son[k]]) son[k] = v;
}
}
void dfs2(int k, int fa, int zu)
{
top[k] = zu;
if(son[k]) dfs2(son[k], k, zu);
for(auto [v, w] : to[k])
{
if(v == fa || v == son[k]) continue;
dfs2(v, k, v);
}
}
int LCA(int x, int y)
{
while(top[x] != top[y])
{
if(depth[top[x]] < depth[top[y]]) swap(x, y);
x = f[top[x]];
}
if(depth[x] > depth[y]) swap(x, y);
return x;
}
int arr[N];
bool cmp(int x, int y){ return dis[x] - dis[flag[x]] > dis[y] - dis[flag[y]]; }
void Solve(int k)
{
sort(arr + 1, arr + k + 1, cmp);
ll ans = dis[arr[1]] - dis[flag[arr[1]]];
int lca = arr[1], mx = dis[arr[1]];
for(int i = 1; i < k; ++i)
{
ans = min(ans, max(mx - dis[lca], dis[arr[i + 1]] - dis[flag[arr[i + 1]]]));
mx = max(mx, dis[arr[i + 1]]);
lca = LCA(lca, arr[i + 1]);
}
ans = min(ans, mx - dis[lca]);
printf("%lld\n", ans);
}
void solve()
{
n = read(), m = read(), q = read();
for(int i = 1; i <= n; ++i)
{
red[i] = 0;
to[i].clear();
}
for(int i = 1; i <= m; ++i)
{
int x = read();
red[x] = 1;
}
for(int i = 1; i < n; ++i)
{
int u = read(), v = read(), w = read();
to[u].emplace_back(pair<int, int>(v, w));
to[v].emplace_back(pair<int, int>(u, w));
}
dfs1(1, 0, 1);
dfs2(1, 0, 1);
while(q--)
{
int k = read();
for(int i = 1; i <= k; ++i) arr[i] = read();
Solve(k);
}
}
signed main()
{
int T = read();
while(T--) solve();
return 0;
}
给定只含有I C P的字符串,一个“好”的字符串定义为其中两种字符个数相等,另一个字符的个数比任何一个字符个数多,求将一个字符划分成若干个“好”的字符串的方案数。
设 \(I_i,C_i,P_i\) 为前 \(i\) 个字符中对应字符的个数。
设 \(dp_i\) 表示前 \(i\) 个字符构成字符串的答案,若能从 \(dp_j\) 转移,需要满足以下条件之一:
\(I_i-I_j=C_i-C_j,P_i-P_j>I_i-I_j\);
\(I_i-I_j=P_i-P_j,C_i-C_j>I_i-I_j\);
\(C_i-C_j=P_i-P_j,I_i-I_j>C_i-C_j\);
移项后得到:
\(I_i-C_i=I_j-C_j,P_i-I_i>P_j-I_j\);
\(I_i-P_i=I_j-P_j,C_i-I_i>C_j-I_j\);
\(C_i-P_i=C_j-P_j,I_i-C_i>I_j-C_j\);
将第二维离散化后可以用树状数组加速,空间复杂度很优,为 \(O(n)\)。
这里使用动态开点线段树实现,时间复杂度和空间复杂度都为 \(O(n\log n)\)。
平衡树的空间复杂度也是 \(O(n)\),感觉快取代线段树了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const ll mod = 998244353;
const int N = 1e6 + 5;
char s[N];
int C[N], I[N], P[N];
void add(int &x, int y){ x = (x + y >= mod) ? (x + y - mod) : (x + y); }
struct Segment_Tree
{
#define ls(x) son[x][0]
#define rs(x) son[x][1]
int son[N * 25][2], tot, root[N * 2], sum[N * 25];
Segment_Tree()
{
memset(root, 0, sizeof(root));
memset(son, 0, sizeof(son));
memset(sum, 0, sizeof(sum));
tot = 0;
}
void update(int &k, int l, int r, int pos, ll val)
{
if(!k) k = ++tot;
add(sum[k], val);
if(l == r) return ;
int mid = (l + r) >> 1;
if(pos <= mid) update(ls(k), l, mid, pos, val);
else update(rs(k), mid + 1, r, pos, val);
}
int query(int k, int l, int r, int L, int R)
{
if(L <= l && r <= R) return sum[k];
int mid = (l + r) >> 1;
if(R <= mid) return query(ls(k), l, mid, L, R);
if(L > mid) return query(rs(k), mid + 1, r, L, R);
return (query(ls(k), l, mid, L, R) + query(rs(k), mid + 1, r, L, R)) % mod;
}
}t1, t2, t3;
ll dp[N];
void solve()
{
scanf(" %s", s + 1);
int n = strlen(s + 1);
for(int i = 1; i <= n; ++i)
{
C[i] = C[i - 1], I[i] = I[i - 1], P[i] = P[i - 1];
if(s[i] == 'C') C[i]++;
if(s[i] == 'P') P[i]++;
if(s[i] == 'I') I[i]++;
}
t1.update(t1.root[n], 0, 2 * n, n, 1);
t2.update(t2.root[n], 0, 2 * n, n, 1);
t3.update(t3.root[n], 0, 2 * n, n, 1);
for(int i = 1; i <= n; ++i)
{
dp[i] = 0;
if(P[i] - I[i] - 1 + n >= 0) dp[i] += t1.query(t1.root[I[i] - C[i] + n], 0, 2 * n, 0, P[i] - I[i] - 1 + n);
if(C[i] - I[i] - 1 + n >= 0) dp[i] += t2.query(t2.root[I[i] - P[i] + n], 0, 2 * n, 0, C[i] - I[i] - 1 + n);
if(I[i] - C[i] - 1 + n >= 0) dp[i] += t3.query(t3.root[C[i] - P[i] + n], 0, 2 * n, 0, I[i] - C[i] - 1 + n);
dp[i] %= mod;
t1.update(t1.root[I[i] - C[i] + n], 0, 2 * n, P[i] - I[i] + n, dp[i]);
t2.update(t2.root[I[i] - P[i] + n], 0, 2 * n, C[i] - I[i] + n, dp[i]);
t3.update(t3.root[C[i] - P[i] + n], 0, 2 * n, I[i] - C[i] + n, dp[i]);
}
printf("%lld\n", dp[n]);
}
signed main()
{
int T = 1;
while(T--) solve();
return 0;
}
2022-2023 ICPC, Asia Yokohama Regional Contest 2022(题解)-CSDN博客
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 105, M = 1e4 + 5;
int n, r[N];
int dp[2][N * M], sum;
void solve()
{
n = read();
for(int i = 1; i <= n; ++i) r[i] = read(), sum += r[i];
dp[0][0] = 1;
for(int i = 1; i <= n; ++i)
for(int j = sum; j >= r[i]; --j)
{
dp[0][j] += dp[1][j - r[i]];
dp[1][j] += dp[0][j - r[i]];
dp[0][j] = min(dp[0][j], 4);
dp[1][j] = min(dp[1][j], 4);
}
if(sum & 1){ printf("No\n"); return ; }
if(dp[0][sum / 2] == 4) printf("Yes\n");
else printf("No\n");
}
signed main()
{
int T = 1;
while(T--) solve();
return 0;
}

浙公网安备 33010602011771号