Educational Codeforces Round 127 (Rated for Div. 2) A~D题

Contest传送门

A.String Building

题意

给定 4 4 4个初始字符串{ a a , a a a , b b , b b b aa,aaa,bb,bbb aa,aaa,bb,bbb},问能否用其中字符串连接成询问的字新字符串(不是每个都要用到)

思路

只要新字符串中相同字母都是连续出现的就是可行的。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

typedef long long LL;
const int N = 2e5 + 7;
const int mod = 1e9 + 7;

void solve() {
  string s;
  cin >> s;

  if (s.size() == 1) {
    cout << "NO" << endl;
    return;
  }

  for (int i = 1; i < s.size() - 1; i++) {
    if (s[i] != s[i - 1] && s[i] != s[i + 1]) {
      cout << "NO" << endl;
      return;
    }
  }

  if (s[0] != s[1] || s[s.size() - 2] != s[s.size() - 1]) {
    cout << "NO" << endl;
    return;
  }
  cout << "YES" << endl;
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

  int t;
  cin >> t;

  while (t--) solve();

  return 0;
}

B.Consecutive Points Segment

题意

给定 x x x轴上的 n n n个点,要求把这些点移到一起形成一段连续区间(点之间不能重叠),操作:对每个 x i x_i xi都可以进行最多一次位移, + 1 +1 +1或者 − 1 -1 1
( 1 ≤ n ≤ 2 ∗ (1≤n≤2* (1n2 1 0 5 10^5 105 ) ) ) , , , ( ∑ n ≤ 2 ⋅ (∑n≤2⋅ (n2 1 0 5 10^5 105 ) ) ) , , , ( 1 ≤ x i ≤ (1≤x_i≤ (1xi 1 0 6 10^6 106 ) ) )

思路

从左端模拟到右端,同时记录前面遍历过的点有没有进行过位移操作,如果相邻两个数不满足条件就可以直接输出 N O NO NO了。

#include <bits/stdc++.h>
#define endl '\n'

using namespace std;

typedef long long LL;
const int N = 2e5 + 7;
const int mod = 1e9 + 7;

int n, a[N], st[N];
// st数组记录第i个数有没有位移过

void solve() {
  cin >> n;

  for (int i = 1; i <= n; i++) st[i] = 0;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }

  bool ff = false;  //记录当前位置左边的数是不是都未位移过

  for (int i = 1; i < n; i++) {
    if (a[i + 1] - a[i] > 3) {
      cout << "NO" << endl;
      return;
    } else if (a[i + 1] - a[i] == 3) {  //应该第i个右移,第i+1个左移
      if (ff) {  //如果左边的数有一个及以上位移过了,那当前操作后就会有空隙
        cout << "NO" << endl;
        return;
      }
      a[i + 1]--;
      a[i]++;
      st[i] = 1;
      st[i + 1] = 1;
      ff = true;
    } else if (a[i + 1] - a[i] == 2) {
      if (ff) {  //优先移左边,左边操作了就移右边
        a[i + 1]--;
        st[i + 1] = 1;
      }
      ff = true;
    }
  }

  cout << "YES" << endl;
}
int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

  int t;
  cin >> t;

  while (t--) solve();

  return 0;
}

C. Dolce Vita

题意

给定 n n n 种商品的原始价格 a i a_i ai,每种商品每天都会贵 1 1 1 元 , 每天有 x x x元去购买商品(每种商品每天最多买一个,钱不会结余),问到最后(即一个都买不起)时最多能买几个商品。
( 1 ≤ n ≤ 2 ⋅ (1≤n≤2⋅ (1n2 1 0 5 10^5 105 ; 1 ≤ x ≤ ; 1≤x≤ ;1x 1 0 9 10^9 109 ) ) ) , , , ( ∑ n ≤ 2 ⋅ (∑n≤2⋅ (n2 1 0 5 10^5 105 ) ) ) , , , ( 1 ≤ a i ≤ (1≤a_i≤ (1ai 1 0 9 10^9 109 ) ) )

思路

首先贪心很好想到,每次都买前几个便宜的商品,所以先对 a i a_i ai排好序后处理前缀和 ,然后再想办法实现一下这个价格上涨的过程就好了。
每种商品每天贵 1 1 1元,其实就是每天拿到的钱会 n n n,所以只要改变 x x x的值,在前缀和数组里二分找当前天能买的个数,累加一下就可以了。
但由于 x x x n n n的值可以差很多,所以还需要对模拟优化一下,假如 x x x很大, a i a_i ai 又都很小,那其实在一段日子里能买的个数是固定的,所以可以直接跳过这些日子,这样就相当于每次模拟能买的个数是严格递减的。
(写的很丑 ,没开long long TLE了好几次)

#include <bits/stdc++.h>
#define endl '\n'

using namespace std;

typedef long long LL;
const int N = 2e5 + 7;
const int mod = 1e9 + 7;

LL n, x, a[N];

void solve() {
  cin >> n >> x;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  sort(a + 1, a + 1 + n);
  for (int i = 1; i <= n; i++) {
    a[i] += a[i - 1];
  }

  LL ans = 0;
  int l = 1, r = n;
  while (l < r) {
    int mid = l + r + 1 >> 1;
    if (a[mid] <= x)
      l = mid;
    else
      r = mid - 1;
  }
  if (a[l] > x) {
    cout << 0 << endl;
    return;
  }
  ans += l;
  x -= n;
  int cnt = 1; //记录第几天
  for (;;) {
    int l = 1, r = n;
    while (l < r) {
      int mid = l + r + 1 >> 1;
      if (a[mid] <= x + (n - mid) * cnt)  //每天扣掉n元后,购买前i个商品时,还要补上多扣的n - i元
        l = mid;
      else
        r = mid - 1;
    }
    if (a[l] > x + (n - l) * cnt) break;
    int ti = (x + (n - l) * cnt - a[l]) / l + 1;  //跳过购买结果一样的日子
    ans += ti * l;
    cnt += ti;
    x -= ti * n;
  }

  cout << ans << endl;
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

  int t;
  cin >> t;

  while (t--) solve();

  return 0;
}

D. Insert a Progression

题意

给定 n n n , x x x ,要求把 1 , 2 , . . . . . , n 1 , 2 , ..... , n 1,2,.....,n 插入到长度为 n n n a a a数组中,使得插入后相邻两位之差的绝对值的和最小
( 1 ≤ n , x ≤ 2 ⋅ (1≤n,x≤2⋅ (1n,x2 1 0 5 10^5 105 ) ) ) , , , ( ∑ n ≤ 2 ⋅ (∑n≤2⋅ (n2 1 0 5 10^5 105 ) ) ) , , , ( 1 ≤ a i ≤ 2 ⋅ (1≤a_i≤2⋅ (1ai2 1 0 5 10^5 105 ) ) )

思路

我们可以惊奇的发现 1 1 1 x x x这几个数中中只有大于原数组中最大值小于原数组中最小值的才会对结果产生影响 , 其他数只要找能形成单调顺序的位子插入就可以,并且结果不会变化。
比如原数组是{ 2 , 7 2,7 2,7} , 插入 1 1 1 8 8 8 , 那么 2 2 2 7 7 7直接按顺序插入到中间就好了,此时和还是 5 5 5
那么剩下的数该怎么做呢。就看小于数组最小值的部分,设当前数为 n u m num num,可以发现有两种情况:接到左端或右端,或者插入到中间两个数 ( ( (假设左小右大 ) ) )之间。插入到中间的贡献是 ( a i + 1 − n u m ) + ( a i − n u m ) − ( a i + 1 − a i ) = (a_{i+1} - num) +(a_i - num) - (a_{i+1}- a_i) = (ai+1num)+(ainum)(ai+1ai)= 2 ∗ ( 2*( 2( a i a_i ai - n u m ) num) num) , 为了使最后的结果尽量小,直接找 a i = M i n a_i = Min ai=Min 插入就可以了。

#include <bits/stdc++.h>
#define endl '\n'

using namespace std;

typedef long long LL;
const int N = 2e5 + 7;
const int mod = 1e9 + 7;

int n, x, a[N];

void solve() {
  cin >> n >> x;
  vector<int> a(n);
  LL tot = 0;
  for (int i = 0; i < n; i++) {
    cin >> a[i];
    if (i) tot += abs(a[i] - a[i - 1]);
  }

  int Max = max_element(a.begin(), a.end()) - a.begin();
  int Min = min_element(a.begin(), a.end()) - a.begin();

  LL ans = tot;

  if (a[Min] > 1) {
    if (a[0] == a[Min] || a[n - 1] == a[Min]) {
      ans += a[Min] - 1;
    } else {
      ans += min(min(a[0], a[n - 1]) - 1, 2 * (a[Min] - 1));
    }
  }
  if (a[Max] < x) {
    if (a[0] == a[Max] || a[n - 1] == a[Max]) {
      ans += x - a[Max];
    } else {
      ans += min(x - max(a[0], a[n - 1]), 2 * (x - a[Max]));
    }
  }

  cout << ans << endl;
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

  int t;
  cin >> t;

  while (t--) solve();

  return 0;
}

累死了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PanC_ake

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值