Codeforces Round 910 A~D(div2-暴力/思维/构造/贪心 补)

链接: https://codeforces.com/contest/1898

A. Milica and String (暴力/模拟)

题意:

给定长度为 n n n A B AB AB 字符串,问 字符串里有 k k k B B B 至少需要多少次操作,该操作是选定一个下标 i i i 使得 s 1 − s i s_1-s_i s1si 的字符变成 A A A B B B,输出操作数、选定的下标和所变成的字符。

思路:

可以观察到数据很小优先考虑暴力模拟。容易发现,如果字符串中 B B B 的个数不足 k k k 个,只需要修改一次就能达到要求,该修改会把前 k − S c n t B k-Scnt_B kScntB A A A覆盖掉。如果字符串中 B B B 的个数超过 k k k 个,只需要修改一次就能达到要求,该修改会把前 S c n t B − k Scnt_B-k ScntBk B B B 覆盖掉。

代码:

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
ll n, m, k, x, y, res = 0, sum = 0, ma = 0, mi = INF;
void solve()
{
    ll n;
    string s;
    cin >> n >> k >> s;
    ll ans = -1;
    char ch = 'A';
    map<char, int> mp;
    for (int i = 0; i < n; i++) {
        mp[s[i]]++;
    }
    int cnt = k - mp['B'];
    if (!cnt) {
        cout << 0 << endl;
        return;
    } else if (cnt < 0) {
        res = 0; //B count
        cnt = -cnt;
        for (int i = 0; i < n; i++) {
            if (s[i] == 'B')
                res++;
            if (res == cnt) {
                ans = i + 1;
                break;
            }
        }

    } else {
        res = 0; //A count
        ch = 'B';
        for (int i = 0; i < n; i++) {
            if (s[i] == 'A')
                res++;
            if (res == cnt) {
                ans = i + 1;
                break;
            }
        }
    }
    cout << "1\n"
         << ans << ' ' << ch << endl;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    // cout.tie(0);
    ll _;
    cin >> _;
    while (_--)
        solve();
    return 0;
}

B. Milena and Admirer(思维/贪心)

题意:

给定长度为 n n n 数字序列,问最少需要多少次分裂操作,可以使得序列非递减。其中分裂操作是将 a i a_i ai 分裂成 x x x a i − x a_i-x aix ,然后放回原位置。

思路:

我们不考虑整个分裂的实时过程,只关心分裂了几次,和上一个分裂操作排在最前的数字是多少,很明显这个数字越大对前面数字影响就会越小。影响是后面影响前面所以遍历应当从后往前。先不考虑分裂成整数,可以想到,将 a i a_i ai 均分成 x x x x < = a i + 1 x<=a_{i+1} x<=ai+1 时是最优的,此时 a i a_i ai 将更新为 c e i l ( x ) ceil(x) ceil(x) ,分裂后的数字数量为 c e i l ( a i / a i + 1 ) ceil(a_i/a_{i+1}) ceil(ai/ai+1) ,分裂操作数即为 c e i l ( a i / a i + 1 ) − 1 ceil(a_i/a_{i+1})-1 ceil(ai/ai+1)1 。( c e i l ceil ceil表示向上取整)

代码:

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
ll n, m, k, x, y, res = 0, sum = 0, ma = 0, mi = INF;
void solve()
{
    cin >> n;
    vector<ll> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[n - i + 1];
    }
    ans = 0;
    for (int i = 2; i <= n; i++) {
        if (a[i] > a[i - 1]) {
            ll tmp = (a[i] + a[i - 1] - 1) / a[i - 1];
            ans += tmp - 1;
            a[i] /= tmp;
        }
    }
    cout << ans << endl;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    // cout.tie(0);
    ll _;
    cin >> _;
    while (_--)
        solve();
    return 0;
}

C. Colorful Grid(思维/构造)

题意:

给定 n ∗ m n*m nm 的点,问从 ( 1 , 1 ) (1,1) (1,1) ( n , m ) (n,m) (n,m) 存不存在线段数为 k k k 的一条连续且红蓝色线段交替的线路,如果有输出 Y E S YES YES 并输出每一行线段的颜色,然后输出每一列线段的颜色,否则输出 N O NO NO R R R 代表红色, B B B 代表蓝色,其中行线段是指 从 ( i , j ) (i,j) (i,j) ( i , j + 1 ) (i,j+1) (i,j+1) 的线段,列线段是指 从 ( i , j ) (i,j) (i,j) ( i + 1 , j ) (i+1,j) (i+1,j) 的线段。存在输出 Y E S YES YES ,否则输出 N O NO NO

思路:

可以想到当 k < n + m − 2 ( 从 ( 1 , 1 ) 到 ( n , m ) 最小线段数 ) k < n+m-2(从 (1,1) 到 (n,m) 最小线段数) k<n+m2((1,1)(n,m)最小线段数) 时不存在。题目重点所说,一个点可以多次经过,然后开始观察
在这里插入图片描述
当存在一个环就可以无限刷 4 4 4 的倍数的步数,所有偶数 m o d 4 mod 4 mod4 结果不是 0 0 0 就是 2 2 2 ,因此要多设置一个弯让路线多走两步,由此可以得出结论,当 k − ( n + m − 2 ) k-(n+m-2) k(n+m2) 为奇数,一定无解,为偶数,只要在最短路线上构造一个环和一个弯即可。

这是官方题解的构造,在起点设一个环,在终点设一个弯。我不同于官方题解的构造。

代码:

#include <bits/stdc++.h>
#define endl '\n'
#define endy {cout<<"YES\n";}//return ;}❤
#define endn {cout<<"NO\n";}//return ;}❤
using namespace std;
typedef long long ll;
ll n, m, k, x, y;
void solve()
{
    cin >> n >> m >> k;
    ll tmp = n + m - 2;
    if (k < tmp || (k - tmp) & 1) {
        endn return;
    }
    endy for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j < m; j++) {
            if (j & 1)
                cout << 'R' << ' ';
            else
                cout << 'B' << ' ';
        }
        cout << endl;
    }
    char ch[] = { 'B', 'R' };
    if (m & 1)
        swap(ch[0], ch[1]);
    for (int i = 1; i < n; i++) {
        for (int j = 1; j <= m; j++) {
            if (m & 1) {
                if (i % 2 == 0 && j == m)
                    cout << ch[0] << ' ';
                else
                    cout << ch[1] << ' ';
            } else {
                if (i % 2 == 0 && j == m)
                    cout << ch[1] << ' ';
                else
                    cout << ch[0] << ' ';
            }
        }
        cout << endl;
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    // cout.tie(0);
    ll _;
    cin >> _;
    while (_--)
        solve();
    return 0;
}

D. Absolute Beauty(思维/贪心)

题意:

给定长度为 n n n 数字序列 a a a b b b ,问只交换一次 b i b_i bi b j b_j bj 得到的 Σ ∣ a i − b i ∣ Σ|a_i-b_i| Σ∣aibi 最大。

思路:

我们只关心怎样情况下的交换会引起增量的变化,把 ∣ a i − b i ∣ |a_i-b_i| aibi ∣ a j − b j ∣ |a_j-b_j| ajbj 看作两条线段,我们发现只有以下三种情况(官方题解的图)
在这里插入图片描述
可以发现,我们只需要让左边的线段的右端点尽可能往左,右边线段的左端点尽可能往右,这时候的增量肯定是最大的。这种时候就有以下四种情况。
在这里插入图片描述
可以发现只需要维护 a i ≤ b i a_i≤b_i aibi m i n ( b ) 、 m a x ( a ) min(b) 、max(a) min(b)max(a) b i ≤ a i b_i≤a_i biai m i n ( a ) 、 m a x ( b ) min(a) 、max(b) min(a)max(b) 。即可求出答案。

代码:

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const ll INF_ = 0x3f3f3f3f3f3f3f3f;
ll n, m, k, ans = 0;
void solve()
{
    cin >> n;
    ll a_ma = 0, b_ma = 0;
    ll a_mi = INF_, b_mi = INF_;
    vector<ll> a(n + 1), b(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    ans = 0;
    for (int i = 1; i <= n; i++) {
        cin >> m;
        ans += (int)abs(a[i] - m);
        if (a[i] > a_ma && a[i] <= m)
            a_ma = a[i];
        if (a[i] < a_mi && a[i] >= m)
            a_mi = a[i];
        if (m < b_mi && a[i] <= m)
            b_mi = m;
        if (m > b_ma && a[i] >= m)
            b_ma = m;
    }
    ll tmp = max(a_ma - b_mi, b_ma - a_mi);
    ll tmp1 = max(b_ma - b_mi, a_ma - a_mi);
    tmp = max(tmp, tmp1);
    if (tmp > 0) {
        ans += 2*tmp;
    }
    cout<<ans<<endl;
}
int main()
{
	ios::sync_with_stdio(0); cin.tie(0);
	// cout.tie(0);
	ll _; cin >> _;
	while (_--) solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值