Codeforces Round 1032(Div. 3)[A-F]
A. Letter Home
题意
给你 \(n\) 个不同的整数放在数组 \(x\) 中,给一个整数s。
你的位置在 \(s\),你可以进行任意次操作一或者二
- 操作一:位置 \(+1\)
 - 操作二:位置 \(-1\)
 
问你经过所有 \(x_i\) 的最小步数
tag: 电梯算法scan
思路
使用电梯调度的算法思想,从 \(s\) 出发计算\(max(先上到最顶部再下到最底部的操作数,先下到最底部再上到最顶部的操作数)\)
Code (C++)
void whx()
{
    cin >> n >> s;
    int ma = -1, mi = 1e18;
    for (int i = 1; i <= n; i ++ ) cin >> x[i], ma = max(ma, x[i]), mi = min(mi, x[i]);
    int ans = 0;
    if (s >= mi && s <= ma)
    {
        ans = min(ma - s + ma - mi, s - mi + ma - mi);
    }
    else
    {
        // debug(s, ma, mi);
        if (s >= ma) ans = s - mi;
        else ans = ma - s;
    }
    cout << ans << "\n";
}
 B. Above the Clouds
题意
给你一个长度为 \(n\) 的字符串 \(s\),仅由小写字母构成。 请判断是否存在三个非空字符串 \(a\) ,\(b\),\(c\) 满足以下条件:
- \(a + b + c = s\)
 - \(b\) 是 字符串 \(a + c\) 的子串
 
tag: 思维
思路
贪心一下,b只选1个字符符合条件的概率最大,如果一个字符都不符合条件,那么选多了更不行。用map记录下来每个字符的个数,然后遍历b可能的字符,如果map存在除b之外的和b相同的字符,那么就存在符合条件的\(a\) ,\(b\),\(c\)。
Code (C++)
void whx()
{
    int n;
    string s;
    cin >> n >> s;
    unordered_map mp;
    for (int i = 0; i < n; i ++ )
    {
        mp[s[i]] ++;
    }
    for (int i = 1; i < n - 1; i ++ )
    {
        if (mp[s[i]] >= 2)
        {
            cout << "Yes\n";
            return;
        }
    }
    cout << "No\n";
} 
 C. Those Who Are With Us
题意
给你一个 \(n\) 行 \(m\) 列的矩阵 \(a\),进行一次操作:
- 选择两个数字 \(1 \leq x \leq n\) 和 \(1 \leq y \leq m\)
 - 对于所有的单元格 \((i, j)\) 中的 \(i = x\) 或 \(j = c\) ,将 \(a_{ij}\) 减 \(1\)
 
求矩阵a进行一次操作后的最小最大值。
tag: 思维
思路
首先的输入时预先存下最大值 \(ma\) 。
不难想到,答案只可能是 \(ma\) 和 \(ma - 1\)。
当我们进行一次操作覆盖了所有的 \(ma\) 后 答案就是 \(ma - 1\),反之 \(ma\)。
如何求一次操作覆盖的 \(ma\) 个数,提前存起来每行每列 \(ma\) 的个数然后枚举一遍就好了。
Code (C++)
void whx()
{
    int n, m;
    cin >> n >> m;
    vector> g(n + 1, vector(m + 1));
    int ma = -1;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            cin >> g[i][j], ma = max(ma, g[i][j]);
    vector x(n + 1), y(m + 1);// 记录每一列最大值的个数
    int cnt = 0;
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= m; j ++ )
        {
            if (g[i][j] == ma)
            {
                cnt ++;
                x[i] ++;
                y[j] ++;
            }
        }
    }
    // 遍历每个十字,如果最大值的个数等于cnt,那么最大值-1,否则不变
    int ans = ma;
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= m; j ++ )
        {
            if (x[i] + y[j] - (g[i][j] == ma) == cnt)
            {
                ans = ma - 1;
            }
        }
    }
    cout << ans << "\n";
}
   
 D. 1709
题意
给你长度为 \(n\) ($ n \leq 40 $)的 \(a\) 数组和 \(b\) 数组,保证从 \(1\) 到 \(2*n\) 的每个整数要么出现在数组 \(a\) 中,要么出现在数组 \(b\) 中。
你最多可以执行 \(1709\) 次以下操作之一:
- 选择索引 \(1 \leq i < n\),\(swap(a_i, a_{i+1})\)。
 - 选择索引 \(1 \leq i < n\),\(swap(b_i, b_{i+1})\)。
 - 选择索引 \(1 \leq i \leq n\),\(swap(a_i,b_i)\)。
 
能否找出 \(a\) 数组和 \(b\) 数组 满足以下两个条件:
- 对于每个 \(1 \leq i < n\) , \(ai \leq a_{i + 1}\) 和 \(b_i<b_{i+1}\) 都成立
 - 对于每一个 \(1 \leq i \leq n\) , \(a_i < b_i\) 成立。
 
tag: 冒泡排序
思路
同时遍历 \(a\) 和 \(b\) 数组,对他进行冒泡排序,记录交换个数。此时满足条件一。
然后检查条件二,遍历一遍,如果 \(a_i < b_i\) 满足,反之交换。
对于 \(a_i > b_i\) ,一定有 \(a_{i + 1} > a_i\) , 所以也有 \(a_{i + 1} > b_i\) ,交换\(a_i,b_i\) 后仍然满足条件一。
复杂度分析: 排序O(\(n^2\)) + 遍历交换 \(a\),\(b\) O(\(n\)) \(=>\) \(1640 < 1709\)
Code (C++)
void whx()
{
    int n;
    cin >> n;
    vector a(n + 1), b(n + 1);
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    for (int i = 1; i <= n; i ++ ) cin >> b[i];
    vector ans;
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= n - i; j ++ )
        {
            if (a[j] > a[j + 1])
            {
                swap(a[j], a[j + 1]);
                ans.push_back({1, j});
            }
            if (b[j] > b[j + 1])
            {
                swap(b[j], b[j + 1]);
                ans.push_back({2, j});
            }
        }
    }
    for (int i = 1; i <= n; i ++ )
    {
        if (a[i] > b[i])
        {
            swap(a[i], b[i]);
            ans.push_back({3, i});
        }
    }
    cout << ans.size() << "\n";
    for (auto& [op, v] : ans) cout << op << " " << v << "\n";
}
  
 E. Sponsor of Your Problems
题意
定义 \(f(a, b)\) 为数字 \(a\) 和 \(b\) 的十进制表示 位数相同的位置数。
例如: \(f(12, 21) = 0\), \(f(31, 37) = 1\), \(f(19891, 18981) = 2\), \(f(54321, 24361) = 3\)。
给你两个长度相同的十进制整数 \(l\) 和 \(r\),考虑所有的 \(l \leq x \leq r\)。找出 \(f(l, x)+ f(x, r)\) 的最小值
tag: 拆位,模拟,分类讨论
思路
情况 \(1\):如果 \(l_i 和 r_i\) 相差大于 \(2\) 直接选择 中间的 那么 就是 \(0\) 个 贡献。
情况 \(2\):如果 $l_i 和 r_i $ 相差等于 \(0\) 那么 必然产生 \(2\) 个 贡献。
情况 \(3\):如果 \(l_i 和 r_i\) 相差等于 \(1\) 需要考虑选 \(l\) 还是 \(r\) 。
从高位向低位考虑。
如果出现情况 \(1\) 那么后续就可以选任意数了, 直接停止计数。
\(l\):1nnnn
\(r\):3nnnn
x的第一位选择了 \(2\),由于 \(x\) 在 l 和 r 之间,所以 \(2....\) 后面可以取任何数。
对于连续的情况 \(2\) ,持续更新贡献 \(+2\)。
如果遇到了情况 \(3\) ,先更新贡献 \(+1\) ,然后考虑选择 \(l\) 还是 \(r\):
一般情况:
128nnn
130nnn
选 2 或者 3 之后都停了
特殊情况:
1299nn
1300nn
选 \(2\) 之后只能选 \(9\)
选 \(3\) 之后只能选 \(0\)
会产生一段连续的贡献再停止
Code (C++)
void whx()
{
    int l, r;
    cin >> l >> r;
    vector a, b;
    while (l)
    {
        a.push_back(l % 10);
        l /= 10;
    }
    while (r)
    {
        b.push_back(r % 10);
        r /= 10;
    }
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    int idx = 0, ans = 0;
    while (a[idx] == b[idx] && idx <= a.size() - 1) ans += 2, idx ++;
    if (idx >= a.size())
    {
        cout << ans << "\n";
        return;
    }
    if ((a[idx] + 1) % 10 == b[idx])
    {
        ans ++, idx ++;
        while (idx <= a.size() - 1)
        {
            bool fa = (a[idx] == 9);
            bool fb = (b[idx] == 0);
            if (!fa)
            {
                cout << ans << "\n";
                return;
            }
            else if (!fb)
            {
                cout << ans << "\n";
                return;
            }
            idx ++, ans ++;
        }
        cout << ans << "\n";
    }
    else
    {
        cout << ans << "\n";
    }
}
 
 F. Yamakasi
题意
给你一个长度为 n 的数组 a ,和两个整数 s ,x 。计算元素之和等于 s 且 最大值等于 x 的数组子段的数目。
具体就是: 输出 \(1 \leq l \leq r \leq n\) 满足如下条件的数对:
- \(a_l + a_{l + 1} + \ldots + a_r = s\).
 - \(\max(a_l, a_{l + 1}, \ldots, a_r) = x\).
 
tag: 前缀和
思路
设 \(a\) 数组 的 前缀和数组 是 \(f\)。
\(s = f[r] - f[l - 1] => f[l - 1] = f[r] - s\)
用map维护 \(r\) 之前的所有 \(f_i\) 的值, 枚举 \(r\),同时查找map中 \(f[r] - s\) 的个数,更新贡献。
同时还要满足一个条件,就是 \([l, r]\) 中 最大值要是 \(x\):
当枚举 \(r\) 的时候 如果 \(a[r] > x\) 那么这一位和前面的位置都不能作为 \(l\) 了,这时候就把 map 清空。
当枚举到一个 \(a[r] = x\) ,把 \(l\) 更新到 \(r - 1\) 位置,更新对应map \(+1\)。
Code (C++)
void whx()
{
    int n, s, x;
    cin >> n >> s >> x;
    vector a(n + 1), f(n + 1);
    for (int i = 1; i <= n; i ++ )
    {
        cin >> a[i];
        f[i] = f[i - 1] + a[i];
    }
    map mp;
    int ans = 0, l = 0;
    for (int r = 1; r <= n; r ++ )
    {
        if (a[r] > x)
        {
            mp.clear();
            l = r;
        }
        else
        {
            if (a[r] == x)
            {
                while (l < r)
                {
                    mp[f[l ++]] ++;
                }
            }
            int cnt = mp[f[r] - s];
            // debug(l, r, cnt);
            ans += mp[f[r] - s];
        }
    }
    cout << ans << "\n";
}
  
 
                
            
        
浙公网安备 33010602011771号