Educational Codeforces Round 118 (Rated for Div. 2)

Educational Codeforces Round 118 (Rated for Div. 2)

知乎体验更佳

A Long Comparison

题目:

给定两个整数a,b然后在后面补上若干个0,比较大小

解决:

减去共同长度的0,然后拼接到a,b后面

用字符串可以直接比较大小

代码:

string a, b;
int x, y;
void work()
{
  cin >> a >> x;
  cin >> b >> y;
  int d = min(x, y);
  x -= d;
  y -= d;
  // cout << a.size() + x << "    " << b.size() + y << endl;
  if (a.size() + x > b.size() + y)
  {
    puts(">");
  }
  else if (a.size() + x < b.size() + y)
  {
    puts("<");
  }
  else
  {
    for (int i = 0; i < x; i ++ ) a += "0";
    for (int j = 0; j < y; j ++ ) b += "0";
    // cout << a << " " << b << endl;
    if (a > b) puts(">");
    else if (a < b) puts("<");
    else puts("=");
  }
}

B Absent Remainder

题目:

给定一个长度为n的序列,让你找到 n / 2(向下取整)对元素(x,y),使得:

  • x != y
  • x y属于序列
  • x % y 不属于序列

1≤ai≤1e6

2≤n≤2⋅1e5

解决:

看数据范围可以知道a[i] >= 1

我们先给序列sort一下。

如果a[1] == 1,那么任意一个数对1取模一定为0,0不存在与序列,合法

如果a[1] > 1, 那么任意一个数a[i] % a[1],得到的数一定是范围[0,a[1]-1]之内的,因为a[1]是最小的数,所以也是合法的

结论,直接输出n/2(下取整)对(a[1], a[i >=2])即可

代码:

int n;
int a[N], c[N];
void work()
{
  scanf("%d", &n);
  memset(c, 0, sizeof c);
  for (int i = 1; i <= n; i ++ )
  {
    scanf("%d", &a[i]);
    c[a[i]] ++;
  }
  sort(a + 1, a + 1 + n);
  int cnt = 0;
  for (int i = 2; i <= n; i ++ )
    if (c[a[i] % a[1]] == 0)
    {
      printf("%d %d\n", a[i], a[1]);
      cnt ++;
      if (cnt == n / 2) break;
    }
}
 

C Poisoned Dagger

题目:

在一个数轴上,有n个防火点,当遍历到当前点时火焰可以从当前点开始向右燃烧,燃烧时间是k秒,每秒燃烧一米,当前点算第一秒

现在时常k我们可以自己控制,问k最小为多少,可以保证燃烧的总长度是h

1≤n≤100;1≤h≤1018

解决:

二分,二分时长k,判断够不够h米

代码:

int n;
LL h;
LL a[N];
bool check(LL mid)
{
  LL ans = 0;
  if (mid > h) return true;
  for (int i = 1; i <= n; i ++ )
  {
    LL r = min(a[i] + mid - 1, a[i + 1] - 1);
    // if (ans + r - a[i] + 1 > h) return true;
    ans += r - a[i] + 1;
    // printf("%lld\n", ans);
    if (ans > h) return true;
  }
  // printf("mid = %lld, ans = %lld, h = %lld\n", mid, ans, h);
  return ans >= h;
}
void work()
{
  // puts("------------------");
  cin >> n >> h;
  for (int i = 1; i <= n; i ++ ) scanf("%lld", &a[i]);
  LL l = 0, r = 1ll * 1e18;
  a[n + 1] = a[n] + h + 1;
  while (l < r)
  {
    LL mid = l + r >> 1;
    if (check(mid)) r = mid;
    else l = mid + 1;
  }
  printf("%lld\n", r);
}

D MEX Sequences

D真的是很精彩的一道题,比赛的时候想到dp了,但是一直没写出来,看了不少代码后才将问题考虑全

题目:

对于任意的ai ,有 |ai - MEX(a1,a2,…,ai)| ≤1

求满足上述式子的方案数

(1≤n≤5⋅105).0≤ai≤n

解决:

我们考虑合法的方案

分为两大种,第一种序列是连续的,第二序列有间段

表示:dp[i][0/1](0表示连续,1表示不连续)

对于第一种:

  • 0自成连续dp[o][0] ++

    样例表示:0

  • 已经有了0~x-1,没有x,可以直接在后面添加x,dp[x][0] = (dp[x - 1][0] + dp[x][0]) % mod

    样例表示:0,1,2,2,2,3,3,…,x-1,x-1 + x

  • 已经有了0~x,也可以直接在后面添加x,dp[x][0] = (dp[x][0] + dp[x][0]) % mod

    样例表示:0,1,2,2,2,3,3,…,x-1,x-1,x,x,x+x

对于第二种:

  • 1单独自成间段dp[1][1] ++

    样例表示:1

  • 已经有了0~x-2,没有x,可以直接在后面添加x,x>=2,dp[x][1] = (dp[x - 2][0] + dp[x][1]) % mod

    样例表示:0,1,2,2,2,3,3,…,x-2,x-2 + x

  • 已经有了0~x,也可以直接在后面添加x,dp[x][1] = (dp[x][1] + dp[x][1]) % mod

    样例表示:0,1,2,2,2,3,3,…,x-2,x-2 , x + x

  • 已经有了0~x,如果后面存在x+2,不管中间出没出现过x+1,都可以将中间的空下,直接放在当前x的后面,dp[x + 2][1] = (dp[x + 2][1] + dp[x + 2][1]) % mod。为什么不是dp[x + 2][1]+=dp[x][1]呢?表示的含义不一样,dp[x][1]可是表示的0,1,2,…,x-2,x-2, …, x , x这种序列,再隔上x+1添加x+2指定不对。

    样例表示:0,1,2,2,2,3,3,…,x-1,x, x, x + 2, x , x + 2, x - 1, x + 2

    0,1,2,4,2,4,3,4

代码:

代码中有注释,并且提供了样例

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 500010;
const LL mod = 998244353;
int n;
int a[N], last[N];
LL dp[N][2];
void work()
{
  cin >> n;
  for (int i = 0; i <= n + 5; i ++ )
  {
    dp[i][0] = dp[i][1] = 0;
    last[i] = -1;
  }
  for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);

  /*
  单个的0算,单个的1算,剩下单个的不算
  从0开始连续的到a[i]算
  从0开始,0,1,...,a[i] - 2, a[i]
   */
  // dp[0][0] = 1;
  for (int i = 1; i <= n; i ++ )
  {
    // 构成的序列中0~a[i]都已经出现过了,现在的a[i]可加可不加所以*2倍
    dp[a[i]][0] = (dp[a[i]][0] + dp[a[i]][0]) % mod;
    // 构成的序列中0~a[i] - 2,a[i]都已经存在了,但是a[i] - 1不存在,这个a[i]可加可不加
    dp[a[i]][1] = (dp[a[i]][1] + dp[a[i]][1]) % mod;
    // 构成的序列中0~a[i]都已经存在了,但是a[i] + 1不存在,如果后面出现a[i] + 2,可以加在这个序列后面
    // 比如合法序列0,1,2,4,2,4,3,4可以供大家食用,拿其中一点,对于第二个2来说,最后一个4可以考虑放在2这个序列后面
    // 也可以不放在2后面这个序列,所以dp[4][1] = dp[4][1] + dp[4][1]
    // 但是,dp[4][1] != dp[2][1] + dp[4][1]因为中间出现了3也就是a[i]-1
    dp[a[i] + 2][1] = (dp[a[i] + 2][1] + dp[a[i] + 2][1]) % mod;
    // 序列已经有了0~a[i] - 1,a[i]可以直接放在a[i]-1的后面
    if (a[i] - 1 >= 0) dp[a[i]][0] = (dp[a[i] - 1][0] + dp[a[i]][0]) % mod;
    else dp[a[i]][0] ++;
    // 序列已经有了0~a[i]-2,但是没有a[i]-1,可以直接将a[i]放在a[i]-2的后面
    if (a[i] - 2 >= 0) dp[a[i]][1] = (dp[a[i] - 2][0] + dp[a[i]][1]) % mod;
    else if (a[i] == 1) dp[a[i]][1] ++;
}
  //这里大家可以输出下样例的结果
  // for (int i = 0; i <= n + 2; i ++ )
  //   cout << dp[i][0] << " ";
  // cout << endl;
  // for (int i = 0; i <= n + 2; i ++ )
  //   cout << dp[i][1] << " ";
  // cout << endl;
  LL ans = 0;
  for (int i = 0; i <= n + 2; i ++ ) ans = (ans + dp[i][0] + dp[i][1]) % mod;
  cout << ans << endl;
}
// 提供两组样例
/*
1
9
0 1 0 1 1 3 0 3 1
答案是133
1
14
1 3 3 0 2 0 1 3 3 3 1 1 3 3
答案是440
 */
int main()
{
  int T; cin >> T;
  while (T -- ) work();
  return 0;
}

下面是是我查阅的代码:

https://codeforces.ml/contest/1613/submission/137715867

https://codeforces.ml/contest/1613/submission/137731034

https://codeforces.ml/contest/1613/submission/137732155

https://codeforces.ml/contest/1613/submission/137733116

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值