2025湖南多校2

Dashboard - 2025 Hunan Multi-School Training Round 2 - Codeforces

D 排序后从中间向两边取。

I,J,B,H参考博客:https://www.cnblogs.com/lyrrr/p/18780739

Problem - K - Codeforces

给定长度为 \(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;
}

Problem - F - Codeforces

给定一棵树,一些节点被染色成红色,一个节点的代价为它与距离它最近的红色祖先节点的距离。

多次询问,每次给定一个集合,至多可以在再将树上的一个节点染成红色,求集合中的点的代价的最小值。

二分答案,此时集合中的一些点不满足条件,将这些点的 \(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;
}

Problem - E - Codeforces

给定只含有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;
}

Problem - A - Codeforces

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;
}
posted @ 2025-03-19 12:13  梨愁浅浅  阅读(78)  评论(0)    收藏  举报