Codeforces Round #783 (Div. 2)

CF系列题解


题目

A. Direction Change

原题链接

A. Direction Change

题意

给定两个整数 n , m n,m n,m,你可以往上下左右四个方向走,但不能连续的走两个相同的方向,求从 ( 1 , 1 ) (1,1) (1,1) ( n , m ) (n,m) (n,m) 的最短路。

输入格式
第一行包含一个整数 t ( 1 ≤ t ≤ 1 0 3 ) t (1≤t≤10^3) t(1t103) ,表示有t组测试数据。
每个测试数据第一行包含三个整数 n , m ( 1 ≤ n , m ≤ 1 0 9 ) n,m (1≤n,m≤10^9) n,m(1n,m109)

输出格式
输出最短路,若不能到达,输出 − 1 -1 1

输入样例:

6
1 1
2 1
1 3
4 2
4 6
10 5

输出样例:

0
1
-1
6
10
17

题解

思路

首先我们先不考虑无解的情况,我们假设 n > m n>m n>m,每次都先走到 ( m i n ( n , m ) , m i n ( n , m ) ) (min(n,m),min(n,m)) (min(n,m),min(n,m)),接下来分类讨论。

a b s ( n − m ) abs(n-m) abs(nm) 为偶数时:
在这里插入图片描述
此时 a n s = ( m − 1 ) × 2 + ( n − m ) / 2 × 4 ans=(m-1)×2+(n-m)/2×4 ans=(m1)×2+(nm)/2×4

a b s ( n − m ) abs(n-m) abs(nm) 为奇数时:
在这里插入图片描述
此时 a n s = ( m − 1 ) × 2 + ( n − m ) / 2 × 4 − 1 ans=(m-1)×2+(n-m)/2×4-1 ans=(m1)×2+(nm)/2×41

考虑无解情况,我们发现只要 n > 1 n>1 n>1 或者 m > 1 m>1 m>1,那么均可以用折线的方式移动从而到达终点,因此我们只需要对 1 1 1 进行特判即可。

代码

#include <bits/stdc++.h>

#define int long long

using namespace std;

using i64 = long long;

void solve()
{
    int n, m;
    cin >> n >> m;

    if (n < m) swap(n, m); // 保证 n > m

    if (m == 1) {
        if (n == 1) {
            cout << "0\n";
        } else if (n == 2) {
            cout << "1\n";
        } else {
            cout << "-1\n";
        }
        return;
    }

    int ans = (m - 1) * 2;
    int cnt = n - m;
    if (cnt % 2) {
        ans += cnt / 2 * 4 + 1;
    } else {
        ans += cnt / 2 * 4;
    }

    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}

B. Social Distance

原题链接

B. Social Distance

题意

给定 m m m 个椅子排成一圈,我们有 n n n 个人且每个人都希望自己两边 a i a_i ai 个人的位置没有人,问能否满足要求。

输入格式
第一行包含一个整数 t ( 1 ≤ t ≤ 5 ⋅ 1 0 4 ) t (1≤t≤5⋅10^4) t(1t5104) ,表示有t组测试数据。
每个测试数据第一行包含两个整数 n , m ( 2 ≤ n ≤ 2 ⋅ 1 0 5 ; 1 ≤ m ≤ 1 0 9 ) n,m (2≤n≤2⋅10^5; 1≤m≤10^9) n,m(2n2105;1m109)
每个测试数据第二行包含 n n n 个整数 a 1 , a 2 , . . . a n ( 1 ≤ a i ≤ 1 0 9 ) a_1, a_2, ... a_n (1≤a_i≤10^9) a1,a2,...an(1ai109)
n n n 的总和不超过 1 0 5 10^5 105

输出格式
若能满足要求输出 YES,否则输出 NO

输入样例:

6
3 2
1 1 1
2 4
1 1
2 5
2 1
3 8
1 2 1
4 12
1 2 1 3
4 19
1 2 1 3

输出样例:

NO
YES
NO
YES
NO
YES

题解

思路

贪心,我们先对 a a a 进行排序,然后从大到小枚举,每次所需要的位置数 a n s = a n s + a i + 1 ans=ans+a_i+1 ans=ans+ai+1,注意边界。

代码

#include <bits/stdc++.h>

#define int long long

using namespace std;

using i64 = long long;

void solve()
{
    int n, m;
    cin >> n >> m;

    vector<int> a(n); 
    for (int i = 0; i < n; i ++ ) cin >> a[i];

    sort(a.begin(), a.end());

    int sum = 0;
    for (int i = n - 1; i > 0; i -- ) { // 不能加到0,因为0要和n-1相邻,需要满足n-1号同学对于距离的需求
        sum += a[i] + 1;
    }
    sum += a[n - 1] + 1;

    if (sum <= m) {
        cout << "YES\n";
    } else {
        cout << "NO\n";
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}

C. Make it Increasing

原题链接

C. Make it Increasing

题意

给定一个序列 a a a b b b 为一个全零序列,每次操作可以使得 b i = b i + a i b_i=b_i+a_i bi=bi+ai 或者 b i = b i − a i b_i=b_i-a_i bi=biai,求使得 b b b 严格单调上升的最少的操作次数

输入格式
第一行包含三个整数 n ( 2 ≤ n ≤ 5000 ) . n (2≤n≤5000). n(2n5000).
第二行包含 n n n 个整数 a 1 , a 2 , . . . , a n ( 1 ≤ a i ≤ 1 0 9 ) a_1, a_2, ..., a_n (1≤a_i≤10^9) a1,a2,...,an(1ai109)

输出格式
一个整数表示最小的操作次数。

输入样例:

5
1 2 3 4 5

输出样例:

4

输入样例:

7
1 2 1 2 1 2 1

输出样例:

10

输入样例:

8
1 8 2 7 3 6 4 5

输出样例:

16

题解

思路

首先我们需要确定一点,一个 b i b_i bi 若是减去 a i a_i ai,那么其必然一直做减法操作,加法亦然,因此必然会出现一个且只有一个 b t b_t bt 是既不需要加也不需要减,即 b t = 1 b_t=1 bt=1,因此我们只需要枚举 t t t 然后将 [ 1 , t − 1 ] [1,t-1] [1,t1] [ t + 1 , n ] [t+1,n] [t+1,n] 变为单调的即可。

细节见代码。

代码

#include <bits/stdc++.h>

#define int long long

using namespace std;

using i64 = long long;

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;

    vector<int> a(n);
    for (int i = 0; i < n; i ++ ) cin >> a[i];

    int ans = 1e18;
    for (int i = 0; i < n; i ++ ) {
        int l = 0, r = 0;
        int res = 0;
        for (int j = i - 1; j >= 0; j -- ) {
            int cnt = l / a[j] + 1; // 保证严格单调增
            l = cnt * a[j];
            res += cnt;
        }
        for (int j = i + 1; j < n; j ++ ) {
            int cnt = r / a[j] + 1;
            r = cnt * a[j];
            res += cnt;
        }
        ans = min(ans, res);
    }

    cout << ans << "\n";

    return 0;
}

D - Optimal Partition

原题链接

D - Optimal Partition

题意

输入格式

输出格式

输入样例:

5
3
1 2 -3
4
0 -2 3 -4
5
-1 -2 3 -1 -1
6
-1 2 -3 4 -5 6
7
1 -1 -1 1 -1 -1 1

输出样例:

1
2
1
6
-1

题解

思路

树状数组优化dp,人麻了,先贴个代码。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

constexpr int N = 5e5 + 10, inf = 1e9;

int n;
int a[N];
i64 s[N];
int tr[N];

int lowbit(int x)
{
    return x & -x;
}

void add(int x, int v)
{
    for (int i = x; i <= n; i += lowbit(i)) tr[i] = max(tr[i], v);
}

int query(int x)
{
    int res = -inf;
    for (int i = x; i > 0; i -= lowbit(i)) res = max(res, tr[i]);
    return res;
}

void init()
{
    for (int i = 0; i <= n; i ++ ) tr[i] = -inf;
}

void solve()
{
    cin >> n;
    init();
    vector<pair<i64, int>> v;
    for (int i = 1; i <= n; i ++ ) {
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
        v.push_back({s[i], -i});
    }
    sort(v.begin(), v.end());
    vector<int> pos(n + 1);
    for (int i = 0; i < n; i ++ ) pos[-v[i].second] = i + 1;

    vector<int> dp(n + 1);
    for (int i = 1; i <= n; i ++ ) {
        dp[i] = dp[i - 1] + (a[i] < 0 ? -1 : a[i] > 0 ? 1 : 0);
        dp[i] = max(dp[i], query(pos[i]) + i);
        if (s[i] > 0) dp[i] = i;
        add(pos[i], dp[i] - i);
    }
    cout << dp[n] << "\n";
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}
#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

constexpr int inf = 1e9;

template <typename T> 
struct BIT {
    const int n;
    vector<T> tr;
    BIT(int n) : n(n), tr(n + 1, -inf) {}
    int lowbit(int x) {
        return x & -x;
    }
    void add(int x, T v) {
        for (int i = x + 1; i <= n; i += lowbit(i)) tr[i] = max(tr[i], v);
    }
    T query(int x) { 
        T res = -inf;
        for (int i = x; i > 0; i -= lowbit(i)) res = max(res, tr[i]);
        return res;
    }
};

void solve()
{
    int n;
    cin >> n;

    vector<int> a(n + 1);
    vector<i64> s(n + 1);
    for (int i = 1; i <= n; i ++ ) {
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
    }

    auto v = s;
    sort(v.begin(), v.end());
    for (int i = 0; i <= n; i ++ ) s[i] = lower_bound(v.begin(), v.end(), s[i]) - v.begin();

    BIT<int> B(n + 1);
    vector<int> dp(n + 1);
    for (int i = 1; i <= n; i ++ ) {
        B.add(s[i - 1], dp[i - 1] - (i - 1));
        dp[i] = max(dp[i - 1] + (a[i] < 0 ? -1 : 0), B.query(s[i]) + i);
    }

    cout << dp[n] << "\n";
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值