刷题之Codeforces Round #783 (Div. 2)

刷题之Codeforces Round #783 (Div. 2)

比赛链接Codeforces Round #783 (Div. 2)

1668A. Direction Change

  • 思路:假设不考虑题目限制的不能连续两步为同一方向,则最少步数为 m + n − 2 m+n-2 m+n2。于是不妨设 n ≤ m n\le m nm,则根据题目限制,最远可到达 ( n , n ) (n,n) (n,n) 位置,后面为了满足要求,在从 ( n , n ) (n,n) (n,n) 走到 ( n , m ) (n,m) (n,m) 的途中,不得不改变方向,需要额外多走。发现你从 ( n , n ) (n,n) (n,n) ( n , n + 2 ) (n,n+2) (n,n+2) 的路线为 ( n , n ) → ( n , n + 1 ) → ( n − 1 , n + 1 ) → ( n − 1 , n + 2 ) → ( n , n + 2 ) (n,n)\rightarrow (n,n+1)\rightarrow (n-1,n+1)\rightarrow (n-1,n+2)\rightarrow (n,n+2) (n,n)(n,n+1)(n1,n+1)(n1,n+2)(n,n+2) 四步。于是可发现规律。
    • 特判 n = 1 n=1 n=1 的情况
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
int cas, n, m, a[N];
int main(){
    // freopen("1.in","r",stdin);
    cin >> cas;
    while(cas--){
        cin >> n >> m;
        if(n > m) swap(n, m);
        if(n == 1){
            if(m <= 2) cout << m - 1 << endl;
            else cout << -1 << endl;
        }
        else cout << n + m - 2 + ((m - n) / 2 * 2) << endl;
    }
}

1668B. Social Distance

  • 思路:显然的思路是将要求间隔相近的人放的一起最优。
    • 于是将所有人按照要求间隔排序,则贪心的想法便是,相邻两个人之间按照要求人数更多的人的要求落座。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
int cas, n, m, a[N];
bool cmp(int a, int b){
    return a > b;
}
int main(){
    // freopen("1.in","r",stdin);
    cin >> cas;
    while(cas--){
        cin >> n >> m;
        rep(i, 1, n) cin >> a[i];
        sort(a + 1, a + 1 + n, cmp);
        ll sum = 0;
        a[0] = a[1];
        rep(i, 1, n) sum += a[i - 1] + 1;
        puts(sum <= m ? "YES" : "NO");
    }
}

1668C. Make it Increasing

  • 思路:观察数据范围,显然可用 O ( n 2 ) O(n^2) O(n2) 的复杂度。
    • 观察样例,发现必定当某个位置为 0 0 0,即不移动时,花费最少。于是可以枚举每个位置为 0 0 0
    • 贪心的计算:当 n o w now now 0 0 0 时,
      • 其右面的数字向右移动为严格单增。考虑 b [ i ] b[i] b[i],则 b [ i − 1 ] + 1 ≤ b [ i ] b[i-1]+1\le b[i] b[i1]+1b[i],于是寻找第一个大于等于 b [ i − 1 ] + 1 b[i-1]+1 b[i1]+1 a [ i ] a[i] a[i] 的倍数为 b [ i ] b[i] b[i]
      • 其左边的数字向左移动为严格单减,则可以理解为向左移动为相反数的严格单增。于是与向右同样计算即可。
      • 最后记录最小值。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const ll LLINF = 0x7fffffffffffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
ll n, a[N], b[N], cnt, ans;
int main(){
    // freopen("1.in","r",stdin);
    cin >> n;
    rep(i, 1, n) cin >> a[i];
    ans = LLINF;
    rep(now, 1, n){
        b[now] = 0, cnt = 0;
        rep(i, now + 1, n){
            ll tmp = b[i - 1] + 1;
            ll add = tmp / a[i] + (tmp % a[i] ? 1 : 0);
            b[i] = add * a[i];
            cnt += add;
        }
        per(i, now - 1, 1){
            ll tmp = b[i + 1] + 1;
            ll add = tmp / a[i] + (tmp % a[i] ? 1 : 0);
            b[i] = add * a[i];
            cnt += add;
        }
        ans = min(ans, cnt);
    }
    cout << ans << endl;
}

1688D. Optimal Partition

  • 算法:树状数组

  • 思路:记 d p [ i ] dp[i] dp[i] 表示前 i i i 个数字可获得的最大答案。记 v i j ( i ≤ j ) v_{ij}(i\le j) vij(ij) 表示 a i + 1 a_{i+1} ai+1 a j a_j aj s s s 值。记 s u m i = ∑ k = 1 i a [ k ] sum_i=\sum\limits_{k=1}^i a[k] sumi=k=1ia[k]

    • 若运用复杂度为 O ( n 2 ) O(n^2) O(n2) 的方法,很容易想到利用前缀和计算出 ∑ k = i + 1 j a [ k ] \sum\limits_{k=i+1}^j a[k] k=i+1ja[k]。通过和 0 0 0 比较得到 v i j v_{ij} vij。再通过判断 v i j v_{ij} vij 利用 d p [ i ] dp[i] dp[i] 来更新 d p [ j ] dp[j] dp[j],更新方法为
      d p [ j ] = max ⁡ ( d p [ i ] + v i j ) , 1 ≤ i ≤ j − 1 dp[j]=\max(dp[i]+v_{ij}),\quad 1\le i\le j-1 dp[j]=max(dp[i]+vij),1ij1
      接着,我们发现若 s u m i < s u m j sum_{i}<sum_j sumi<sumj,则有 v i j = j − ( i + 1 ) + 1 = j − i v_{ij}=j-(i+1)+1=j-i vij=j(i+1)+1=ji,即 d p [ j ] = d p [ i ] + j − i dp[j]=dp[i]+j-i dp[j]=dp[i]+ji,变形为
      d p [ j ] − j = d p [ i ] − i \color{red}dp[j]-j=dp[i]-i dp[j]j=dp[i]i
      这时,我们便可以利用树状数组存储 d p [ i ] − i dp[i]-i dp[i]i 来在 1 ≤ i ≤ j − 1 1\le i\le j-1 1ij1 O ( log ⁡ n ) O(\log n) O(logn) 计算 d p [ j ] dp[j] dp[j]

    • 由于我们要存储 v i j v_{ij} vij 级别的数字,这 1 e 9 1e9 1e9 的数量级不好存储。因此我们将前缀和离散化变为 p o s [ i ] pos[i] pos[i],是和 n n n 相同的 5 e 5 5e5 5e5 级别的数量来存储。由于存储的是 d p [ i ] − i dp[i]-i dp[i]i,因此我们每次计算 d p [ j ] dp[j] dp[j] 时都要计算 1 ≤ i ≤ j − 1 1\le i\le j-1 1ij1 中形成的答案,即 i + s u m ( p o s [ j ] − 1 ) i+sum(pos[j]-1) i+sum(pos[j]1)

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
#define lowbit(x) x & -x
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 5e5 + 5;
const db eps = 1e-10;
int cas, n, m;
ll a[N];
ll sum[N], tmpsum[N];
int pos[N], dp[N];
struct Fenwick{
    const int n;
    vector<int> h;
    Fenwick(int n) : n(n), h(n + 5, -INF) {}
    void add(int x, int val){
        for(int i = x; i <= n; i += lowbit(i)){
            h[i] = max(h[i], val);
        }
    }
    int sum(int x){
        int ans = -INF;
        for(int i = x; i >= 1; i -= lowbit(i)){
            ans = max(ans, h[i]);
        }
        return ans;
    }
};
int main(){
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> cas;
    while(cas--){
        cin >> n;
        tmpsum[0] = sum[0] = 0;
        rep(i, 1, n){
            cin >> a[i];
            sum[i] = sum[i - 1] + a[i];
            tmpsum[i] = sum[i];
        }
        //前缀和离散化(因为前缀和数字太大,会使树状数组所开空间太多,装不下)
        sort(tmpsum, tmpsum + 1 + n);  //从 0 到 n 排序
        rep(i, 0, n){
            pos[i] = lower_bound(tmpsum, tmpsum + 1 + n, sum[i]) - tmpsum + 1;
        }
        //初始化树状数组
        Fenwick fen(n + 1);
        dp[0] = 0;
        fen.add(pos[0], dp[0] - 0);
        rep(i, 1, n){
            dp[i] = dp[i - 1] + (a[i] > 0 ? 1 : (a[i] < 0 ? -1 : 0));  //初始当前dp[i]
            dp[i] = max(dp[i], i + fen.sum(pos[i] - 1));  //计算当前dp[i]
            fen.add(pos[i], dp[i] - i);  //更新
        }
        cout << dp[n] << endl;
    }
}
/*
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
*/

1688E. Half Queen Cover

  • 类型:构造题
  • 思路:图中红色位置放置皇后。首先左上角放置可控制黄色区域,接着右下角放置控制最后一行和一列。最后填补中间位置控制蓝色区域。根据模 3 3 3 考虑,
    • n = 3 x + 2 n=3x+2 n=3x+2 时, a n s = 2 x + 1 ans=2x+1 ans=2x+1。此时不需要右下角绿色区域,我们在左上斜着放 x + 1 x+1 x+1 个,右下斜着放 x x x 个可全部控制。
    • n = 3 x + 3 / 3 x + 1 n=3x+3/3x+1 n=3x+3/3x+1 时,额外多出的 1    o r    2 1\;or\;2 1or2 行可直接在右下角落填充 1    o r    2 1\;or\;2 1or2 个方块。于是可删除这 1    o r    2 1\;or\;2 1or2 行和列,再根据上面的方法处理 n = 3 x + 2 n=3x+2 n=3x+2 的情况(最好根据代码找找规律)
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
int n, ans;
int main(){
    // freopen("1.in","r",stdin);
    cin >> n;
    ans = n / 3 * 2 + (n % 3 ? 1 : 0);
    cout << ans << endl;
    if(ans == 1){
        cout << "1 1" << endl;
        return 0;
    }
    int k = n / 3;
    if(n % 3 != 2) k--;
    while(n % 3 != 2){
        cout << n << " " << n << endl;
        n--;
    }
    //k+1个
    rep(i, 1, k + 1) cout << i << " " << k + 2 - i << endl;
    //k个
    rep(i, 1, k) cout << n + 1 - i << " " << n - k + i << endl;
}
/*
8
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值