牛客周赛 Round 154

A 小红的类型转换

涉及知识:无

思路:因为小数点后面全是 \(0\),所以编译器输出的时候会帮我们省略小数点和 \(0\) 的,直接输出即可

Code
点击查看代码
void solve()
{
    cin>>x;
    cout<<x<<endl;
}

B 小红的矩阵构造

涉及知识:贪心

思路:不难想到第一行正向排列,第二行逆向排列即为最优解

Code
点击查看代码
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cout<<i<<' ';
    }
    cout<<endl;
    for(int i=n;i>=1;i--)
    {
        cout<<i<<' ';
    }
}

C 小红的序列删除

涉及知识:模拟,贪心

思路:我们可以设最终剩余字符串中 \(U,D,L,R\) 的数量分别为\(C_U,C_D,C_L,C_R\)
最终的坐标为 \(X_0= C_R 一C_L,Y_0= C_U 一C_D\),我们需要最大化的距离为 \(D=|C_R一C_L|+|C_u-C_D|\),做法很显然,就是把这个绝对值式子分情况拆开各自求最优即可

Code
点击查看代码
void solve()
{
    cin >> n >> k >> s;
    int cnt[256] = {0};
    for (char c : s)
    {
        cnt[c]++;
    }
    vector<pair<string, string>> a = {
        {"LD", "RU"},
        {"LU", "RD"}, 
        {"RD", "LU"}, 
        {"RU", "LD"} 
    };
    int ans = -1;
    int cnt2[256] = {0};
    for (auto str : a)
    {
        int cnt3[256];
        for (int i = 0; i < 256; i++)
            cnt3[i] = cnt[i];
        int res = k; 
        for (char c : str.first)
        {
            int t = min(res, cnt3[c]);
            cnt3[c] -= t;
            res -= t;
        }
        for (char c : str.second)
        {
            int t = min(res, cnt3[c]);
            cnt3[c] -= t;
            res -= t;
        }
        x = cnt3['R'] - cnt3['L'];
        y = cnt3['U'] - cnt3['D'];
        int d = abs(x) + abs(y);
        if (d > ans)
        {
            ans = d;
            for (int i = 0; i < 256; i++)
            {
                cnt2[i] = cnt3[i];
            }
        }
    }
 
    for (char c : s)
    {
        if (cnt2[c] > 0)
        {
            cout << c;
            cnt2[c]--;
        }
    }
}

D 小红的01串

涉及知识:思维

思路:先计算出最初的结果,然后再考虑操作对于结果的影响,不难发现,对整个区间的翻转实际上只对边界处产生了影响,所以对于每次操作只需要处理区间左右端点即可

Code
点击查看代码
void solve()
{
    int q;
    cin >> n >> q >> s;
    vi d(n);
    int ans = 0;
    for (int i = 0; i < n; i++)
    {
        if (s[i] != s[(i + 1) % n])
        {
            d[i] = 1;
        }
        ans += d[i];
    }
    while (q--)
    {
        cin >> l >> r;
        int li = (l - 1 + n) % n;
        int ri = r;
        if (li != ri)
        {
            ans -= d[li];
            d[li] ^= 1;
            ans += d[li];
            ans -= d[ri];
            d[ri] ^= 1;
            ans += d[ri];
        }
        cout << ans << endl;
    }
}

E 小红的子数组删除

涉及知识:树状数组

思路:因为 \(k\) 是固定的,且要求为连续数组,所以不难想到用滑动窗口,对于不在窗口内的元素,维护他们的中位数即可,对于维护中位数这种 "第 \(k\) 大"问题,不难想到用树状数组维护每移动一位,对左右端点的元素进行增加和删除,判断中位数是否为 \(x\)

Code
点击查看代码
vi tr, b;
int len, ans;
void add(int i, int num)
{
    for (; i <= len; i += i & -i)
    {
        tr[i] += num;
    }
}
int que(int num)
{
    int idx = 0;
    for (int j = 19; j >= 0; j--)
    {
        int cur = idx + (1 << j);
        if (cur <= len && tr[cur] < num)
        {
            idx = cur;
            num -= tr[cur];
        }
    }
    return idx + 1;
}
void check()
{
    if (m % 2 == 1)
    {
        int v = que((m + 1) / 2);
        if (b[v - 1] == x)
            ans++;
    }
    else
    {
        int v1 = que(m / 2);
        int v2 = que(m / 2 + 1);
        if (b[v1 - 1] + b[v2 - 1] == 2 * x)
            ans++;
    }
}
void solve()
{
    cin >> n >> k >> x;
    vi a(n);
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    m = n - k;
    if (m == 0)
    {
        cout << 0 << endl;
        return;
    }
    b = a;
    sort(all(b));
    b.erase(unique(all(b)), b.end());
    len = b.size();
    tr.resize(len + 1, 0);
    ans = 0;
    for (int i = k; i < n; i++)
    {
        int it = lower_bound(all(b), a[i]) - b.begin() + 1;
        add(it, 1);
    }
    check();
    for (int i = 0; i < n - k; i++)
    {
        int it = lower_bound(all(b), a[i]) - b.begin() + 1;
        add(it, 1);
        int it2 = lower_bound(all(b), a[i + k]) - b.begin() + 1;
        add(it2, -1);
        check();
    }
    cout << ans << endl;
}

F 艾雅法拉的点燃

涉及知识:dp

思路:老样子,贪心是显然不对的,所以考虑 \(dp\),我们设第 \(i\) 个敌人被当做“点燃”主目标的次数为 \(c_i\),那么,第 \(i\) 个敌人受到的总点燃伤害为:总点燃伤害=(自己身上的)+(左边溅射的)+(右边溅射的),如果这个伤害不够把第 \(i\) 个敌人打死,剩下的血只能用普通攻击来补刀,需要补的普攻次数就是 \(a_i\)-总点燃伤害。当我们决定到第 \(i\) 个人时,为了计算第 \(i-1\) 个人有没有死透(是否需要普攻补刀),我们需要知道:第 \(i-2\) 个人的点燃次数(记为 \(u\)),第 \(i-1\) 个人的点燃次数(记为 \(j\)),第 \(i\) 个人的点燃次数(记为 \(k\)),此时,第 \(i-1\) 个人的受到的伤害就彻底确定了:\(2*j+u+k\)。也就可以立刻算出 \(i一1\)需要多少钱补刀了
DP 状态定义:
dp[当前考虑到第 \(i\) 个人][第 \(i-1\) 人的点燃次数 \(j\)][第 \(i\) 人的点燃次数 \(k\)]=此时的最小总花费,通过枚举点燃次数更新 \(dp\) 数组即可

Code
点击查看代码
void solve()
{
    cin >> n >> x >> y;
    vi a(n + 2, 0);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    int dp[2][27][27];
    for (int i = 0; i <= 26; i++)
    {
        for (int j = 0; j <= 26; j++)
        {
            dp[0][i][j] = INF;
        }
    }
    dp[0][0][0] = 0;
    for (int i = 1; i <= n + 1; i++)
    {
        int cur = i % 2;
        int t = (i - 1) % 2;
        for (int j = 0; j <= 26; j++)
        {
            for (int k = 0; k <= 26; k++)
            {
                dp[cur][j][k] = INF;
            }
        }
        int mx, mx2, mx3;
        if (i <= n)
            mx = (a[i] + 1) / 2;
        else
            mx = 0;
        if (i - 1 >= 1)
            mx2 = (a[i - 1] + 1) / 2;
        else
            mx2 = 0;
        if (i - 2 >= 1)
            mx3 = (a[i - 2] + 1) / 2;
        else
            mx3 = 0;

        for (int j = 0; j <= mx2; j++)
        {
            for (int k = 0; k <= mx; k++)
            {
                int mi = INF;
                for (int u = 0; u <= mx3; u++)
                {
                    if (dp[t][u][j] == INF)
                        continue;
                    int c = 2 * j + u + k;
                    int res = 0;
                    if (i - 1 >= 1)
                    {
                        res = a[i - 1] - c;
                        if (res < 0)
                            res = 0;
                    }
                    int temp = dp[t][u][j] + res * x;
                    if (i <= n)
                    {
                        temp += y * k;
                    }

                    if (temp < mi)
                    {
                        mi = temp;
                    }
                }
                dp[cur][j][k] = mi;
            }
        }
    }

    int ans = INF;
    int mx2 = (a[n] + 1) / 2;
    for (int j = 0; j <= mx2; j++)
    {
        if (dp[(n + 1) % 2][j][0] < ans)
        {
            ans = dp[(n + 1) % 2][j][0];
        }
    }

    cout << ans << endl;
}
posted @ 2026-07-26 21:08  Lambda_L  阅读(32)  评论(0)    收藏  举报