首页 > 其他分享 >Educational Codeforces Round 164 (Rated for Div. 2)

Educational Codeforces Round 164 (Rated for Div. 2)

时间:2024-04-15 17:45:16浏览次数:14  
标签:std Educational Rated int sum Codeforces times le pos

目录

写在前面

比赛地址:https://codeforces.com/contest/1954

本来有机会上大分但是唐了 E 没调出来呃呃。

小号比大号分高了呃呃以后想休闲直接打大号了哈哈

A

数学。

若要将 \(n\) 个位置全部涂成颜色 \(i\),则一定要修改 \(n - \operatorname{count}(i)\) 次。

则一个显然想法是最小化 \(\max_{1\le i\le m} \operatorname{count}(i)\),由鸽巢原理可知 \(\max_{1\le i\le m} \operatorname{count}(i) = \left\lceil \frac{n}{m} \right\rceil\)。

则当且仅当 \(k < n - \left\lceil \frac{n}{m} \right\rceil\) 输出 YES,否则 NO

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    int n, m, k; std::cin >> n >> m >> k;
    std::cout << ((k < n - ceil(1.0 * n / m)) ? "YES" : "NO") << "\n";
  }
  return 0;
}

B

枚举。

手玩下发现有解当且仅当原 beatuiful 数列所有位置并不全相等,且由性质可知,此时数列一定满足:

  • 修改为全部相等数列后,所有位置均为 \(a_1\)。
  • 不存在两个连续的不等于 \(a_1\) 的位置。

记所有非 \(a_1\) 的位置为 \(p_1, p_2, \cdots, p_k\),则将数量修改为非 beautiful 有两种方案:

  • 将 \(p_i\) 调整到数列首或数列尾。
  • 调整使得两个 \(p_i\) 相邻。

考虑记 \(p_0 = 0\),\(p_{k + 1} = n + 1\),则答案即为:

\[\min_{1\le i\le k + 1} \left(p_{i} - p_{i - 1} - 1\right) \]

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 3e5 + 10;
//=============================================================
int n, a[kN];
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n;
    std::vector<int> pos;
    pos.push_back(0);
    for (int i = 1; i <= n; ++ i) {
      std::cin >> a[i];
      if (a[i] != a[1]) pos.push_back(i);
    }
    pos.push_back(n + 1);
    if (pos.size() == 2) {
      std::cout << -1 << "\n";
    } else {
      int ans = n;
      for (int i = 1, sz = pos.size(); i < sz; ++ i) {
        ans = std::min(ans, pos[i] - pos[i - 1] - 1);
      }
      std::cout << ans << "\n";
    }
    
  }
  return 0;
}
/*
1
8
1 1 2 1 2 1 1 1
*/

C

数学,贪心。

考虑有这样两个数:

\[\begin{cases} x = \sum\limits_{i = 1}^{n} 10^{n - i}\times x_i \\ y = \sum\limits_{i = 1}^{n} 10^{n - i}\times y_i \end{cases}\]

考虑拆一下多项式乘法,则有:

\[\begin{aligned} x \times y &= \left(10^{n - 1} \times x_1 + \sum\limits_{i = 2}^{n} 10^{n - i}\times x_i\right) \times \left(10^{n - 1} \times y_1 + \sum\limits_{i = 2}^{n} 10^{n - i}\times y_i \right)\\ &= 10^{2n - 2}\times x_1 y_1 + 10^{n - 1} \times \sum\limits_{i = 2}^{n} 10^{n - i}\times (y_1 x_i + x_1 y_i) + \left(\sum\limits_{i = 2}^{n} 10^{n - i}\times x_i\right)\times \left(\sum\limits_{i = 2}^{n} 10^{n - i}\times y_i\right) \end{aligned}\]

发现上式中第一项不受修改影响,因为有与指数相乘的原因,第二项的影响比第三项大得多,于是仅需考虑按照位数递减,最大化第二项的影响即可。

于是一个显然的想法是从高到低枚举各位 \(i(1\le i\le n)\),找到第一个 \(x_i \not= y_i\) 的位置 \(i\)。记第 \(i\) 位较大的数为 \(A\),另一个数为 \(B\),由上式可知为了最大化 \((y_1x_i + x_1y_i)\) 仅需调整使得 \(j>i\) 各位 \(j\) 中满足 \(B_j > A_j\) 即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 110;
//=============================================================
std::string x, y;
bool tag[kN];
//=============================================================
void modify(int pos_, int len_, std::string &s_, std::string &t_) {
  for (int i = pos_; i < len_; ++ i) {
    if (s_[i] > t_[i]) {
      std::swap(s_[i], t_[i]);
    }
  }
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> x >> y;
    for (int i = 0, len = x.length(); i < len; ++ i) tag[i] = 0;
    for (int i = 0, len = x.length(); i < len; ++ i) {
      if (x[i] == y[i]) continue;
      else if (x[i] > y[i]) modify(i + 1, len, x, y);
      else modify(i + 1, len, y, x);
      break;
    }
    std::cout << x << "\n" << y << "\n";
  }
  return 0;
}

D

数学,DP。

妈的真有用到同一个结论的题啊哈哈

考虑对于一组选出的球 \(b_1, b_2, \cdots b_{m}\) 应如何分组才可令分组数量最少。

  • 发现若无法令各组均为 2,则剩下的球一定为数量最多的一种。
  • 则一种最优的分配方案是每次选出数量最多的一种,与数量最少的一种,然后从中各选出一个配成一组。

这个分配方式怎么这么熟悉?翻了下刚打的湖南多校发现有道还更强的题:https://codeforces.com/gym/512144/problem/C,要求将 \(n\) 种颜色的物品按每组 \(k\) 个分组,则有结论最少分组数为:

\[\max\left( \max {b_i}, \left\lceil \dfrac{\sum b_i}{k} \right\rceil\right) \]

在本题中 \(k=2\)。

则若已知选择的所有球中数量最多的球的数量,再考虑球的总数即可求得最少分组数。发现题目给定额外性质:\(\sum a_i \le 5000\),球的总数很少,于是考虑直接大力枚举 \(\max b_i\),再大力枚举球的个数,并考虑有多少种方案选出满足条件的一组球。

于是先将 \(a\) 升序排序,然后考虑 DP,记 \(f_{i, j}\) 表示从升序排序后的 \(a_1\sim a_n\),选出满足 \(\sum b_i = j\) 的子序列 \(b\) 的方案数,\(g_i\) 表示钦定选出的最大数量的球为 \(i\) 的对答案的贡献。初始化 \(f_{0, 0} = 1\),则有:

\[\begin{aligned} &\forall 1\le i\le n, a_i\le j,\ f_{i, j} = f_{i - 1, j - a_i}\\ &\forall 1\le i\le n,\ g_{i} = \sum_{a_i\le j} f_{i, j}\times \max\left( a_i, \left\lceil \dfrac{j}{2} \right\rceil\right) \end{aligned}\]

答案即为 \(\sum_{1\le i\le n} g_i\)。

发现这个式子太背包了,套路地滚动数组即可。总时间复杂度 \(O(n\times \sum a_i)\) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 5010;
const LL p = 998244353;
//=============================================================
int n, s, a[kN];
LL ans, sum[kN];
//=============================================================
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n;
  for (int i = 1; i <= n; ++ i) {
    std::cin >> a[i];
    s += a[i];
  }

  sum[0] = 1;
  std::sort(a + 1, a + n + 1);
  for (int i = 1; i <= n; ++ i) {
    for (int j = s; j >= a[i]; -- j) {
      LL size = std::max(1ll * a[i], (LL)ceil(1.0 * j / 2.0));
      LL delta = sum[j - a[i]] * size % p;
      ans = (ans + delta) % p;
      
      sum[j] = (sum[j] + sum[j - a[i]]) % p;
    }
    // std::cout << ans << "---\n";
  }
  std::cout << ans << "\n";
  return 0;
}

E

枚举,数据结构。

唉场上实现拉了没做出来亏死。

手玩发现每次操作后数列的变化形式如下:

  • 进行若干次操作后,某些位置变为非正,数列被分为若干独立的子区间。
  • 对每个子区间独立地重复上述操作。

发现操作的顺序是无所谓的,甚至操作的对象也是无所谓的,因为每次操作会影响整个极大区间,可以保证每次合法的操作都是有贡献的。

于是考虑每轮按顺序枚举当前所有区间,然后对当前所有区间仅进行一次操作。发现当第 \(i\) 轮操作结束时,当前所有区间一定为满足 \(a_j> k\times i\) 的极大区间。考虑记满足 \(a_j > m\) 的所有极大区间数量为 \(\operatorname{count}(m)\),则对于某个确定的 \(k\),答案可以形式化地表示为:

\[\sum_{1\le k\times i < \max a} \operatorname{count}(k\times i) \]

发现上式是一个调和级数的形式,可以 \(O(v\ln v)\) 地枚举所有有贡献的项,仅需考虑如何快速预处理 \(\operatorname{count}\)。考虑通过按权值递减不断地向数列中加数,在此过程中维护有多少个极大区间 \(c\)。如当前插入到权值为 \(v\) 的位置 \(p\),考虑加入 \(p\) 后会使得区间数量增加,还是会合并当前某些区间导致区间数量减少:

  • 首先令 \(c:= c+ 1\)。
  • 检查数列中 \(p\) 的前驱 \(\operatorname{pre}_p\):若 \(\operatorname{pre}_p = p - 1\) 或不存在则令 \(c:=c - 1\)。
  • 检查数列中后继 \(\operatorname{next_p}\):若 \(\operatorname{next}_p = p + 1\) 或不存在则令 \(c:=c - 1\)。

上述过程可直接使用 set 维护,预处理后再调和级数地枚举贡献并求得答案即可。则总时间复杂度 \(O(v\ln n + v\ln v)\) 级别,其中 \(v = \max a_i\)。

注意为了防止 RE,必须先在 set 中插入极小极大值。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int n, maxa, a[kN];
std::vector <int> pos[kN];
std::set<int> s;
LL nowcnt, cnt[kN], ans[kN];
//=============================================================
void init() {
  s.insert(-kN);
  s.insert(kN);
}
void insert(int pos_) {
  ++ nowcnt;
  std::set<int>::iterator next = s.upper_bound(pos_);
  std::set<int>::iterator pre = std::prev(next);
  if ((*pre) == pos_ - 1) -- nowcnt;
  if ((*next) == pos_ + 1) -- nowcnt;
  s.insert(pos_);
} 
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n;
  for (int i = 1; i <= n; ++ i) {
    std::cin >> a[i];
    maxa = std::max(maxa, a[i]);
    pos[a[i]].push_back(i);
  }

  init(); 
  for (int i = maxa; i; -- i) {
    for (auto p: pos[i]) insert(p);
    cnt[i] = nowcnt;
  }
  // for (int i = 1; i <= maxa; ++ i) std::cout << cnt[i] << " ";
  
  for (int i = maxa; i; -- i) {
    for (int j = 0; i * j < maxa; ++ j) {
      ans[i] += 1ll * cnt[i * j + 1];
    }
  }
  for (int i = 1; i <= maxa; ++ i) std::cout << ans[i] << " ";
  return 0;
}

F

咕咕~

写在最后

学到了什么:

  • D:真能做到原啊草
  • E:set 查询元素,求前驱后继,可用于添加数列元素过程中区间合并。为了防止 RE 必须先在 set 中插入极小极大值。

标签:std,Educational,Rated,int,sum,Codeforces,times,le,pos
From: https://www.cnblogs.com/luckyblock/p/18136594

相关文章

  • [题解](A-G)Atcoder Educational DP Contest(更新中)
    AtcoderEducationalDPContest\(\textbf{A.Frog1}\)对于一块石头\(i(3\lei\leN)\),\(i-1\)和\(i-2\)均能到达。用\(f[i]\)表示跳到第\(i\)个石头用的最小体力消耗:\[f[i]=min(abs(h[i]-h[i-1])+f[i-1],abs(h[i]-h[i-2])+f[i-2])\qquadi\ge3\]时间复杂度\(O(n)\)。......
  • 【官方题解】Codeforces Round 939 (Div. 2)
    CodeforcesRoundAyachiNeneSolutions(div.2)A.Nene'sGameIdea:[user:Otomachi_Una]Solution不难发现如果一个人初始位置\(p\geqa_1\),那么必然会在有限次回合当中被踢出。答案即为\(\min(n,a_1-1)\)。B.NeneandtheCardGameIdea:[user:Otomachi_Una]Hint......
  • Educational Codeforces Round 164 (Rated for Div. 2) D
    因为理解错了题目导致我没错出来。我理解为有两个人取球,每个人每次都是取一组,也就是一组的球必须要放在一个人手里。。因为我之前做的一个背包是这个样子的。这就导致了,我认为每次转移所需要的信息是比实际要的多很多的,直接导致我没法设计一个正常的dp。然后炸了。。。好烦。。......
  • [CF1954] Educational Codeforces Round 164 (Rated for Div. 2) 题解
    [CF1954]EducationalCodeforcesRound164(RatedforDiv.2)题解A.PaintingtheRibbon最优策略是染\(\lceil\dfrac{n}{m}\rceil\)个颜色,然后Bob会把剩下的都染成这个颜色voidwork(){intn,m,k,c;cin>>n>>m>>k;c=(n+m-1)/m;......
  • [CF1942] CodeTON Round 8 (Div. 1 + Div. 2, Rated, Prizes! A~E 题解
    [CF1942]CodeTONRound8(Div.1+Div.2,Rated,Prizes!A~E题解A.FarmerJohn'sChallenge只有两种情况,一种是单调递增,这时\(k=1\),另一种是所有数都相同,这时\(k=n\)。B.BessieandMEX首位可以确定,然后从前往后增量构造\(p\)即可。voidwork(){cin>>......
  • Educational Codeforces Round 36 (Rated for Div. 2)
    EducationalCodeforcesRound36(RatedforDiv.2)A直接枚举即可#include<bits/stdc++.h>intmain(){std::ios::sync_with_stdio(false);std::cin.tie(nullptr);intn,k;std::cin>>n>>k;intans=1e9;for(int......
  • Educational Codeforces Round 154 (Rated for Div. 2)
    B和C写的太慢了。吃了不该吃的罚时,C还莫名其妙的T了一发,另一发也是不应该T的。B连想了两个假做法,然后甚至都实现了,然后过不了样例,再基于这两个才想到了真做法。当时的思路已经有些模糊了,但是确实是写的太慢了,而且\(O(n^2)\)的限制给的也很宽裕,但是我居然还傻乎乎的去先\(O(n^2)......
  • Codeforces Round 893 (Div. 2) D
    链接第一个想法:\(O(n^2)\)可过,很明显,我可以直接统计出来每一个位置作为中心,向两边扩展最多能得到的多少个连续的1。这个想法是不成熟的,但是我甚至开始写了。哎。然后写了140行,发现寄了,思路太复杂,完全用不了。这里就引出了一个事情:太复杂的思路其实不能算是思路,因为表达是不可能......
  • Codeforces Round 938 (Div. 3) A-H 题解
    A-YogurtSaleSolution当\(2a>b\)时,显然用\(a\)来买两个比较好,否则就用\(b\)来买两个比较好Code#include<bits/stdc++.h>usingnamespacestd;voidsolve(){intn;cin>>n;inta,b;cin>>a>>b;b=min(b,a*2);int......
  • Codeforces-182E 题解
    Vasyahasrecentlyboughtsomelandanddecidedtosurrounditwithawoodenfence.Hewenttoacompanycalled"Woodenboard"thatproduceswoodenboardsforfences.Vasyareadinthecatalogofproductsthatthecompanyhasatitsdisposal\(......