Grakn Forces 2020 ABCDE题解
看到老外评论区中说,这场的难度估计是\(div.1\)和\(div.1.5\)的合并
A. Circle Coloring #构造
题目链接
题意
给定三个长度为\(n\)数组\(a,b,c\),要你从三个数组中选取元素构造出长度也为\(n\)的数组,内部相邻元素互不相等(包括下标\(1\)和\(n\))
分析
注意是一个圈中相邻元素互不相等。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 105;
const double EPS = 1e-7;
int q, n;
int a[MAXN], b[MAXN], c[MAXN], ans[MAXN];
int main(){
scanf("%d", &q);
while(q--){
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
for (int i = 0; i < n; i++) scanf("%d", &b[i]);
for (int i = 0; i < n; i++) scanf("%d", &c[i]);
for (int i = 0; i < n; i++){
ans[i] = a[i];
if((ans[i] == ans[(i + 1) % n]) || (ans[i] == ans[(i - 1 + n) % n])){
ans[i] = b[i];
if((ans[i] == ans[(i + 1) % n]) || (ans[i] == ans[(i - 1 + n) % n])){
ans[i] = c[i];
}
}
}
for (int i = 0; i < n; i++){
printf("%d%c", ans[i], (i == n - 1) ? '\n' : ' ');
}
}
}
B. Arrays Sum #贪心 #构造 #双指针
题目链接
题意
给定一个非降数组\(a\),需分解出若干个同\(a\)长度的子数组\(b_i\)(其中每个数组中的元素种类不超过给定的\(k\)),设分解出的子数组数量为\(m\),使得对于\(a_j(1\leq j\leq n)=b_{1,j} + b_{2,j} + ... + b_{m,j}\),现要你求出最小的\(m\)。(若找不出,则输出\(m\))
分析
考虑到数据规模比较小,我们不妨每次先从当前\(a\)的每个元素中取出最小元素,直到不能再分解为止,由此得到的子数组最多含有两种元素(一种是当时的最小值,(存在前导零的情况)一种是\(0\))。
接下来,\(lo\)指向含\(0\)少的子数组,\(tot\)指向当前含\(0\)多的子数组。依次将当前含\(0\)多的子数组(相应地\(tot\)指针往上移动)归入到含\(0\)少的子数组,直到\(lo\)指向数组的元素种数已到达要求,将\(lo\)指向下一个含\(0\)少的子数组。当两指针相遇时,算法即终止。这里之所以要先将含\(0\)较多的子数组归入含\(0\)少的子数组,这样是保证遍历时的顺序性的同时缩减规模。
我把我的思路做成下图:
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>
#include <unordered_map>
using namespace std;
const int MAXN = 105;
int q, n, k, a[MAXN];
bool check[MAXN]; //check[i],表示分解的子数组bi是否存在前导零
int delByMin(){
int diff = 0;
bool f = false;
for (int i = 1; i <= n; i++) {
if(a[i] == 0)
f = true; //说明当前数组已经存在前导零
else {
diff = a[i]; //取得当前数组中第一个非零元素(即最小非零元素)
break;
}
}
if (diff == 0) return -1; //说明当前数组无非零元素
for (int i = 1; i <= n; i++) {
if (a[i] != 0) a[i] -= diff; //用最小非零元素去减当前数组
}
return f;
}
int main() {
scanf("%d", &q);
while (q--) {
scanf("%d%d", &n, &k);
int kin = 0; a[0] = -1;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if (a[i - 1] != a[i]) kin++; //统计原数组有多少种元素
}
if (k == 1 && kin > k) { printf("-1\n"); continue; }
if (kin <= k) { printf("1\n"); continue; }
int tot = 0, lo = 0;
while (1) {
int f = delByMin(); if(f == -1) break;
tot++; //分解数量
check[tot] = f; //标记分解出的子数组有前导零
}
while (lo < tot) {
lo++;
int diff = min(k - 1 - check[lo], tot - lo);
tot -= diff; //合并
}
printf("%d\n", lo);
}
}
C. Discrete Acceleration #二分答案 #模拟
题目链接
题意
路长为\(l\)m,两车分别位于坐标\(0\)点,坐标\(l\)点,并以\(1m/s\)相向而行。路上有\(n\)个位于不同坐标的加速包,车一遇到即能速率\(+1\),问两车何时相遇。允许误差不超过\(1e-6\)
分析
起初我尝试直接\(O(n)\),但是发现有不少细节要考虑,尤其是当两车速率不再相等的同时,一边车能遇到密集的加速包,另一边车只遇到几个加速包,十分麻烦。
不妨二分答案,即二分两车相遇的时间\(t\),然后分别计算两车在\(t\)s内的路程\(x1、x2\),如果\(x1+x2<l\),说明两车在\(t\)s内无法相遇,将\(t\)下界拔高;反之,将\(t\)上界降低。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
const double EPS = 1e-7;
int q, n, l, flag[MAXN];
bool Judge(double t) {
int idx = 0;
double x1 = 0, x2 = 0, curv = 1, tt = t;
while (t > EPS && idx <= n) { //计算左车路程(从左到右)
idx++;
double diff = ((double)flag[idx] - x1); //求出下一加速包到当前坐标的距离
x1 += min((diff * 1.0 / curv), t) * curv;
t -= min((diff * 1.0 / curv), t);
curv++; //加速
}
idx = n + 1; curv = 1;
while (tt > EPS && idx >= 1) {//计算右车路程(从右到左)
idx--;
double diff = (l - (double)flag[idx] - x2);
x2 += min((diff * 1.0 / curv), tt) * curv;
tt -= min((diff * 1.0 / curv), tt);
curv++;
}
return (x1 + x2 >= l);
}
int main() {
scanf("%d", &q);
while (q--) {
scanf("%d%d", &n, &l);
for (int i = 1; i <= n; i++) scanf("%d", &flag[i]);
flag[0] = 0; flag[n + 1] = l;
double lo = 0, hi = l;
while ((hi - lo) > EPS) {
double mid = (lo + hi) * 1.0 / 2;
if (Judge(mid)) hi = mid;
else lo = mid;
}
printf("%lf\n", lo);
}
}
D. Searchlights #动态规划
题目链接
题意
\(n\)个强盗分别位于坐标\((x_1,y_1),(x_2,y_2),...\),\(m\)个探照灯分别位于\((c_1,d_1),(c_2,d_2),...\)。所有强盗的移动是同步的,每次移动可以向右一步或向上一步。若存在某个强盗\(i\),他坐标中\(x_i\leq c_j\)同时\(y_i\leq d_j\),则该强盗被探照灯\(j\)发现,说明不安全。你要求出最少的移动步数,使得所有强盗都安全。
分析
参考了官方题解,思考了好久qaq。
我们假设所有强盗都向右移动\(x\),向上移动\(y\)后保证安全。显然可以推出,这样的\(x,y\),对于任意强盗\((x_i,y_i)\)、任意探照灯\((c_i,d_i)\),满足其中一个条件:要么\(x+x_i>c_i\),要么\(y+y_i>d_j\)。如果前一条件不满足的话(即此时\(x\leq c_i-x_i\)),那么\(y>d_j-y_i即y_i\geq d_j+b_i+1\)。注意到\(c_i-x_i\leq2\times 10^6\),我们不妨定义一个数组\(diff\),当某个强盗\(i\)在前一条件不满足的情况下,专门记录他向右移动\(c[j] - x[i]\)(解决\(i\)的安全情况)之后,还需要向上移动(保证其他强盗也安全)的距离\(d[j] - y[i] + 1\),为了应对多种探照灯的情况,必须找出强盗\(i\)向上移动的最大距离。
接下来我们就要找出“移动尽可能小的\(x\)步的同时移动尽可能小的\(y\)步”的情况,这样的情况即是题目要求。但注意代码中为何要从大到小枚举移动\(x\)距离呢?别忘了对于\(x,y\)的定义,这样是保证大部分强盗移动\(y\)步距离后能够安全的前提下,逐步缩小\(x\)步距离。
可能表达的思路还不够准确 ,未来还会过来更新下自己的理解。
#include <string>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
#include <deque>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int MAXN = 2005;
int n, m, x[MAXN], y[MAXN], c[MAXN], d[MAXN];
int diff[2000005];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d%d", &x[i], &y[i]);
for (int j = 1; j <= m; j++) scanf("%d%d", &c[j], &d[j]);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (c[j] >= x[i]) //说明某一探照灯的x坐标比强盗i的x坐标大,不安全
diff[c[j] - x[i]] = max(diff[c[j] - x[i]], d[j] - y[i] + 1);//更新y坐标增量
}
}
int ans = 2000005, dy_max = 0;
for (int dx = 1000005; dx >= 0; dx--) { //向后枚举所有x增量
dy_max = max(dy_max, diff[dx]); //取得后缀中y增量的最大值(要保证y必须满足的前提)
ans = min(ans, dx + dy_max); //当前的x增量 与 后缀y增量最值 更新 移动次数
}
printf("%d\n", ans);
}
E. Avoid Rainbow Cycles #并查集
题目链接
题意
给定\(m\)长度的整数数组\(a\)、\(n\)长度的整数数组\(b\)、\(m\)个整数集合:\(A_1,A_2,...,A_m\),每个集合元素在\(1,2,...,n\)范围内。每次操作中,如果你删除\(A_i\)中元素\(j\)(注意,不是第\(j\)个元素),要付出\(a_i+b_j\)的代价。删完后,对每一集合\(A_i\)内部所有元素两两建边\(x\leftrightarrow y(其中x<y)\),且这些边颜色均为\(i\)。现要你用最少的代价建图,保证图中不存在一种含不同颜色边的环。
分析
我们可以发现,如果要保证图中不存在彩色环,就说明不可能有两个顶点,同时属于多个集合(说明两顶点间有多种颜色的边),最多只能属于一个集合。在建图的过程中,如果遇到这种情况,那就应该从代价小的集合中删去这两点,只在代价最大的集合保留这两点。那么问题就进一步转化为判断某一点是否属于多个颜色集合了,显然需要并查集。
由上面的分析,我们不用将每个集合中任意满足要求的两点进行建边,而是将颜色集合与普通整数点进行建边(边权取自删除该点所耗费的代价),数据规模减少了。先建边权大的边,一旦发现某个点已经加入某个颜色集合的同时又属于另一颜色集合,则删去这另一颜色集合,并计入耗费的代价。
#include <string>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
#include <deque>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
int n, m;
int a[MAXN], b[MAXN], tot = 0, fa[MAXN << 1];
struct Edge{
int u, v, w;
} E[MAXN << 1];
bool cmp(const struct Edge& a, const struct Edge& b){
return a.w > b.w;
}
int findSet(int x){
if(x != fa[x]) fa[x] = findSet(fa[x]);
return fa[x];
}
int main(){
scanf("%d%d", &m, &n);
for (int i = 1; i <= m; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
for (int i = 1, sz; i <= m; i++){ //第i个集合(即第i种颜色)
scanf("%d", &sz);
for(int j = 1, x; j <= sz; j++){ //A_i的第j个元素
scanf("%d", &x);
E[++tot] = {i, m + x, a[i] + b[x]}; //此处的x要加上m,是避免顶点编号与集合编号重合
}
}
for (int i = 1; i <= n + m; i++) fa[i] = i;
sort(E+1, E+1+tot, cmp); //按代价从大到小排序
ll ans = 0;
for (int i = 1; i <= n + m; i++) fa[i] = i;
for (int i = 1; i <= tot; i++){
int pu = findSet(E[i].u), pv = findSet(E[i].v);
if(pu != pv) //说明顶点v之前未加入任何一个颜色集合
fa[pv] = pu;
else //说明v在两种颜色集合中,会出现彩虹环,要删
ans += (ll)E[i].w;
}
printf("%lld\n", ans);
return 0;
}
Grakn Forces 2020 ABCDE题解的更多相关文章
- CF Grakn Forces 2020 1408E Avoid Rainbow Cycles(最小生成树)
1408E Avoid Rainbow Cycles 概述 非常有趣的题目(指解法,不难,但很难想) 非常崇拜300iq,今天想做一套div1时看见了他出的这套题Grakn Forces 2020,就 ...
- Grakn Forces 2020
比赛链接:https://codeforces.com/contest/1408 A. Circle Coloring 题意 给出三个长为 $n$ 的序列 $a,b,c$,对于每个 $i$,$a_i ...
- Codeforces Round #353 (Div. 2) ABCDE 题解 python
Problems # Name A Infinite Sequence standard input/output 1 s, 256 MB x3509 B Restoring P ...
- Codeforces Round #460 (Div. 2) ABCDE题解
原文链接http://www.cnblogs.com/zhouzhendong/p/8397685.html 2018-02-01 $A$ 题意概括 你要买$m$斤水果,现在有$n$个超市让你选择. ...
- Codeforces Round #546 (Div. 2) ABCDE 题解
1136A: 题意:一本书有n个章节,每个章节的分别在li到ri页,小明读完书后将书折在第k页,问还有多少章节没有读 题解:控制k在li~ri的范围内后输出n-i即可 #include <set ...
- JOI 2020 Final 题解
T1. 只不过是长的领带 大水题,把 \(a_i,b_i\) 从小到大排序. 发现最优方案只可能是大的 \(a_i\) 跟大的 \(b_i\) 匹配,小的 \(a_i\) 与小的 \(b_i\) 匹配 ...
- DISCO Presents Discovery Channel Code Contest 2020 Qual题解
传送门 \(A\) 咕咕 int x,y; int c[4]={0,300000,200000,100000}; int res; int main(){ cin>>x>>y; ...
- 【Nowcoder 上海五校赛】二数(模拟)
题目描述: 我们把十进制下每一位都是偶数的数字叫做“二数”. 小埃表示自己很聪明,最近他不仅能够从小数到大:2,3,4,5....,也学会了从大数到小:100,99,98...,他想知道从一个数开始数 ...
- 【Home Page】本博客使用指南
[关于] 坐标:ZJ.HZ.XJ. 高一现役 OIer,经常被吊打. Luogu:_Wallace_ [近期] 浙大 ICPC-ACM 2020 部分题解: 关键字「ZJU-ICPC Summer T ...
随机推荐
- Java学习的第十五天
1.今天复习了第四章的内容 重新看了看方法参数问题,final修饰的关键字 2.今天没问题 3.明天学习多态变化
- 删除指定路径下指定天数之前(以文件的创建日期为准)的文件:BAT + REG + Ritchie Lawrence 日期函数
代码如下: @echo off::演示:删除指定路径下指定天数之前(以文件的创建日期为准)的文件.::如果演示结果无误,把del前面的echo去掉,即可实现真正删除.::本例调用了 Ritchie L ...
- 技术总监的故事告诉大家,要学会say【NO!】
今天就给大家分享一个发生在我自己身上的事情吧. 1 2015年的时候,我和我的领导A,还有几个小伙伴正在做一个"紧急定制",这个任务是公司老大CEO和重要客户定下来的一个项目,背后 ...
- 微信小程序-TodoList
TodoList 博客班级 https://edu.cnblogs.com/campus/zjcsxy/SE2020 作业要求 https://edu.cnblogs.com/campus/zjcsx ...
- 833. Find And Replace in String —— weekly contest 84
Find And Replace in String To some string S, we will perform some replacement operations that replac ...
- The Product-Minded Software Engineer
转自The Product-Minded Software Engineer Product-minded engineers are developers with lots of interest ...
- 双十一,就用turtle画个单身狗送给自己
今年的双十一到了 但还有谁记得双十一是 单身狗的节日 单身狗的我是时候站出来 捍卫自己的权益了 单身是一种怎样的状态? 我们所有人都单身过,但也许只有很少的人真正体验过. 短视频内容完全是假的,全程是 ...
- linux常用命令和系统基本目录
Linux 基础命令及基本目录 一.网卡 1.网卡配置文件路径 /etc/sysconfig/network-scripts/ifcfg-eth0 配置文件: TYPE=Ethernet # 以太 ...
- pc端兼容IE9及以上版本
最近业务部门反映我们商城的兼容性不是很好,尤其是在IE浏览器上,经过调研,我们决定对IE9及以上版本的IE内核浏览器进行主流程测试,发现有哪些功能在IE9上不兼容 一.CSS兼容性 如下图所示 使用了 ...
- 【QT】 Qt多线程的“那些事”
目录 一.前言 二.QThread源码浅析 2.1 QThread类的定义源码 2.2 QThread::start()源码 2.3 QThreadPrivate::start()源码 2.4 QTh ...