Spectral::Cup 2026 Round 1 (Codeforces Round 1094, Div. 1 + Div. 2)做题报告和补题

赛时 2 题

A. A Wonderful Contest

思路:观察数据范围很小,直接暴力做

B. Artistic Balance Tree

知识点:思维

思路:观察变换情况,发现变换前后坐标的奇偶性不会变,而且对于任何一个 \(x_i\),可以把任何同奇偶坐标的元素移动到这个位置进行标记,所以我们把奇偶坐标处所对应的元素分别存起来;任务是找出所有未标记元素的最小和,也就是让标记元素的和最大,所以计算有多少个 \(x_i\) 是奇数,多少是偶数,从各自序列里取正数就行了,如果取完了正数,因为可以反复标记,不标记负数,只标记正数就可以了,(特殊情况是序列里全是负数,但必须取一个,取最大的就行了)

Code
点击查看代码
void solve()
{
    cin >> n >> m;
    vi a(n);
    int sum = 0;
    vi ji, ou;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
        sum += a[i];
        if ((i + 1) % 2 == 1)
        {
            ji.push_back(a[i]);
        }
        else
        {
            ou.push_back(a[i]);
        }
    }

    int j = 0, o = 0;
    for (int i = 0; i < m; i++)
    {
        cin >> x;
        if (x % 2 == 1)
        {
            j++;
        }
        else
        {
            o++;
        }
    }
    sort(ji.rbegin(), ji.rend());
    sort(ou.rbegin(), ou.rend());
    int rj = 0;
    if (j > 0 && !ji.empty())
    {
        rj = ji[0];
        int mx = min(j, (int)ji.size());
        for (int i = 1; i < mx; i++)
        {
            if (ji[i] > 0)
            {
                rj += ji[i];
            }
            else
            {
                break;
            }
        }
    }
    int ro = 0;
    if (o > 0 && !ou.empty())
    {
        ro = ou[0];
        int mx = min(o, (int)ou.size());
        for (int i = 1; i < mx; i++)
        {
            if (ou[i] > 0)
            {
                ro += ou[i];
            }
            else
            {
                break;
            }
        }
    }
    int ans = sum - (rj + ro);
    cout << ans << endl;
}

C. Median Partition

知识点:dp

思路:先看数据范围,是 \(5e3\),可以进行 \(O(n^2)\) 的算法,注意到这个题不能排序,如果贪心着做容易漏情况,所以看看 dp 的做法
枚举区间的起点,同时枚举上一个区间的终点,如果该区间是合法的,那应该满足以下条件
\( \left\{\begin{matrix} 大于mid的数的二倍小于区间长度, & \\ 小于mid的数的二倍小于区间长度, & \\ 区间长度为奇数,& \end{matrix}\right . \)
如果我们分别预处理出大于和小于 \(mid\) 的数的前缀和,就可以在 \(O(1)\) 的时间内判断区间是否合法

Code
点击查看代码
void solve()
{
    cin >> n;
    vi a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    vi b = a;
    sort(b.begin() + 1, b.end());
    int mid = (n + 1) / 2;
    int num = b[mid];
    vi p1(n + 1), p2(n + 1);
    for (int i = 1; i <= n; i++)
    {
        p1[i] = p1[i - 1];
        p2[i] = p2[i - 1];
        if (a[i] >= num)
        {
            p1[i]++;
        }
        if (a[i] <= num)
        {
            p2[i]++;
        }
    }
    vi dp(n + 1, -1);//因为区间必须相邻,所以初始时除0外所有点作为末端都是不合法的
    dp[0] = 0;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= i; j++)
        {
            if ((i - j) & 1)
            {
                int len = i - j;
                if (p1[i] - p1[j] > len / 2 && p2[i] - p2[j] > len / 2 && dp[j] != -1)
                {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            else
            {
                continue;
            }
        }
    }
    cout << dp[n] << endl;
}

D. Permutation Construction

知识点:前缀和,思维

思路:对于题目中所说的价值,可翻译为 \(s_{j-1}-s_{i-1}\),也就是前缀和相减,那么该如何构造呢?对于前缀和 \(s_{i-1}\) 越大的位置,我们越要利用这个大值来和别的构成逆序对获得更大的收益,所以对于最大前缀和,给它赋值最小的 \(1\) 是最合理的,以此类推
注意:原题目中所说的{i,j}为逆序对,说的是排列中的下标{i,j},也就是满足 i<j&&p_i>p_j的一对数

点击查看代码
struct node
{
    int val, id;
};
bool cmp(node a, node b)
{
    if (a.val == b.val)
    {
        return a.id < b.id;
    }
    return a.val > b.val;
}
void solve()
{
    cin >> n;
    vi a(n + 1);

    vector<node> p(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        p[i].val = p[i - 1].val + a[i - 1];//计算 s[i-1]
        p[i].id = i;
    }
    sort(p.begin() + 1, p.end(), cmp);
    vi res(n + 1);
    for (int i = 1; i <= n; i++)
    {
        res[p[i].id] = i;
    }
    for (int i = 1; i <= n; i++)
    {
        cout << res[i] << endl;
    }
    cout << endl;
}
posted @ 2026-04-26 01:02  Lambda_L  阅读(91)  评论(0)    收藏  举报