【题解】Codeforces Round #798 (Div. 2)
本篇为 Codeforces Round #798 (Div. 2) 也就是 CF1689 的题解,因本人水平比较菜,所以只有前四题
A.Lex String
题目描述
原题面
给定两个字符串 \(a,b\),要求通过以下的两种操作构造一个新的字符串 \(c\),使得 \(c\) 的字典序最小,并且要求不能连续使用某一种操作超过 \(k\) 次
1.从 \(a\) 中任选一个字符插入到 \(c\) 的末尾
2.从 \(b\) 中任选一个字符插入到 \(c\) 的末尾
若某一个字符串为空则认为构造结束。
若字符串 \(x\) 比字符串 \(y\) 字典序小则必定满足以下两种情况之一:
1.\(x\) 为 \(y\) 的前缀,且 \(x \not= y\)
2.从左到右的顺序,\(x\) 与 \(y\) 第一个不同的位置上 \(x\) 的字符的字典序比 \(y\) 的字符的字典序小
题目分析
我们首先可以发现一点:所谓的插入末尾与插入开头并没有任何的区别
所以为了字典序小我们肯定每一次选择 \(a,b\) 中字典序最小的字符,如果超过了 \(k\) 次就选另一个字符串的,下一次再选它就好了
需要注意这里的字典序小不包含:仅仅是字符串长度更小,所以不用考虑这样的构造出的字符串长度上的问题
代码详解
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
char a[MAXN],b[MAXN];
int main(){
int t;
cin>>t;
while(t--){
int n,m,k;
cin>>n>>m>>k;
for(int i=1; i<=n; i++){
cin>>a[i];
}
for(int i=1; i<=m; i++){
cin>>b[i];
}
sort(a+1,a+n+1);
sort(b+1,b+m+1);
int l = 1,r = 1,tmp = 0;
bool flag = false;
while(l <= n && r <= m){
if(a[l] < b[r]){
if(flag){
cout<<a[l++];
flag = false;
tmp = 1;
}
else{
if(tmp < k){
cout<<a[l++];
tmp++;
}
else{
cout<<b[r++];
tmp = 1;
flag = true;
}
}
}
else if(a[l] >= b[r]){
if(!flag){
cout<<b[r++];
flag = true;
tmp = 1;
}
else{
if(tmp < k){
cout<<b[r++];
tmp++;
}
else{
cout<<a[l++];
tmp=1;
flag = false;
}
}
}
}
cout<<endl;
}
return 0;
}
其实代码有一点复杂,但是其实思路很简单
B. Mystic Permutation
题目描述
原题面
给定一个 \([1,n]\) 的排列,要求你构造一个 \([1,n]\) 的排列,使得这两个排列之间的任意一个相同位置的元素都不相同,且满足这个排列的字典序最小。
如果无法构造出这样的序列则输出 -1
题目分析
一个非常正常的想法:把元素从小到大插入,如果遇到了相同的或者用过的就跳过
但是这样会出现一种情况那就是序列的最后一个元素是 \(n\),那么放到最后一个元素的时候就只剩下了 \(n\),那么就无论如何也无法放置了
这个问题也非常好解决:最后的这个元素 \(n\) 我们肯定不能放在很靠前的位置,因为这样很明显字典序不会最小了,那么为了使得这个元素放得下我们就放在 \(n-1\) 的位置上,让 \(n-1\) 的位置上原本应该放的元素放到 \(n\) 上,这样能保证前 \(n-2\) 个一定是最小的,而不存在一种别的方法使得可以使得最后的两个元素比我们现在放置的更小而且前 \(n-2\) 个依旧保持最小。
有一说一样例是真强,不然的话我应该发现不了这一点。
代码详解
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3+5;
bool flag[MAXN];
int a[MAXN];
int main(){
int t;
cin>>t;
while(t--){
memset(flag,false,sizeof(flag));
int n;
cin>>n;
if(n == 1){
cin>>a[1];
printf("-1\n");
continue;
}
for(int i=1; i<=n; i++){
cin>>a[i];
}
for(int i=1; i<=n; i++){
if(i == n - 1 && !flag[a[n]]){
printf("%d ",a[n]);
flag[a[n]] = true;
}
for(int j=1; j<=n; j++){
if(!flag[j] && j != a[i]){
printf("%d ",j);
flag[j] = true;
break;
}
}
}
cout<<endl;
}
return 0;
}
因为数据范围比较小,所以为了写的来更加舒服就写了这样非常暴力的做法,但是我们应该理解起来更容易一些。
C. Infected Tree
题目描述
原题面
给定一棵以 \(1\) 号节点为根的二叉树,现在 \(1\) 号节点感染了病毒,病毒每一回合都会去感染与该节点直接相连的节点,而你在这一回合里可以选择删除任意一个没有被病毒感染的点,这样就断开了它与其直接相连的点得关系,询问最多可以有多少不被病毒感染的点,被删除的点不算做不被病毒感染的点
题目分析
我们考虑为了尽可能多的保留节点,我们每一次肯定是会删除被病毒感染的节点的某个子节点,而删除之后我们的问题就可以转化为它的另一个儿子(注意为二叉树)根被感染病毒能保留多少节点的子问题,那么这很明显就可以考虑一下 \(DP\)。\(DP\) 状态也很简单:\(dp[i]\) 表示以 \(i\) 为根的子树若 \(i\) 被感染病毒,最多能保留多少节点
那么下面就是考虑转移了,转移很明显就是我们考虑删除哪一个 \(i\) 的子节点就好了,因为是二叉树所以不用考虑更多的情况。那么我们就分别考虑究竟删除哪一个子节点,假设删除 \(son_1\),那么相当于在 \(son_1\) 的子树里我们可以保留下 \(size[son_1] - 1\) 个节点,\(size[i]\) 代表以 \(i\) 为根的子树的大小(包含节点 \(i\)),而在 \(son_2\) 的子树里,我们依旧可以保留下 \(dp[son_2]\) 个节点,所以最终的状态就是:
\]
需要注意可能该子树为空,那么 \(size[son] - 1\) 就是一个负数,所以需要对 \(0\) 取 \(\max\),避免这种情况
代码详解
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5+5;
struct edge{
int nxt,to;
edge(){}
edge(int _nxt,int _to){
nxt = _nxt,to = _to;
}
}e[2 * MAXN];
int ans = 0,cnt,head[MAXN],dp[MAXN],sz[MAXN];
//dp[now] 表示以 now 为根的子树,假设 now 被感染,最多保留多少个
void add_edge(int from,int to){
e[++cnt] = edge(head[from],to);
head[from] = cnt;
}
void dfs(int now,int fa){
sz[now] = 1;
int son[3] = {0,0,0};
int tot = 0;
for(int i=head[now]; i;i = e[i].nxt){
int to = e[i].to;
if(to == fa) continue;
son[++tot] = to;
dfs(to,now);
sz[now] += sz[to];
}
//去 son[1] / 去 son[2]
dp[now] = max(dp[son[1]] + max(sz[son[2]] - 1,0),dp[son[2]] + max(sz[son[1]] - 1,0));
}
int main(){
int t;
cin>>t;
while(t--){
memset(head,0,sizeof(head));
memset(dp,0,sizeof(dp));
memset(sz,0,sizeof(sz));
ans = 0;
int n;
cin>>n;
for(int i=1; i<n; i++){
int from,to;
cin>>from>>to;
add_edge(from,to);
add_edge(to,from);
}
dfs(1,0);
printf("%d\n",dp[1]);
}
return 0;
}
先进行 \(dfs\) 再更新 \(sz\) 和 \(dp\),以及多组数据所以每次需要将各种数组清零,清零 \(head\) 数组也相当于清零边的数组了
D. Lena and Matrix
题目描述
原题面
给定一个 \(n\times m\) 的矩阵,每个位置都有一个颜色,分别为黑色或白色,要求你选择一个位置(不计这个位置上是什么颜色),使得整个位置到所有黑色格子的曼哈顿距离的最大值最小。
曼哈顿距离即:\(|x_1 - x_2| + |y_1 - y_2|\)
题目分析
要求的是最大值最小,其实发现这个最大值不是和任意一个黑色格子的距离都有可能成为最大值的,能成为最大距离的黑色格子一定是最左上、右下、左下、右上的黑格子,可以发现如果不是这四个格子,那么把距离转化到这四个黑色格子上都一定可以增加
下面就是如何寻找着四个黑色格子的问题了,我感觉这个思路还是很神奇的。
考虑如果一个格子离左上角越近,\(x\) 越小,\(y\) 越小,而且 \(x\) 与 \(y\) 的减小,对其离左上角的距离产生的贡献是一样的,那么就直接令这个贡献为 \(x+y\),那么最左上角的点也就一定是贡献最小的点,最右下角的点也一定是贡献最大的点,这就解决了两个。
考虑如果一个格子里右上角越近,\(x\) 越大,\(y\) 越小,而且它们产生的贡献也都是一样的,所以就考虑直接令贡献为 \(x-y\),这样最右上的点也就是 \(x-y\) 最大点,最左下的点也就是 \(x-y\) 最小的点,至此四个点全部解决了。
那么就是要找一个位置使得这个位置到这四个位置的距离最大值最小,那么就直接枚举每一个位置寻找一下就好了
代码详解
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int INF = 1e9+5;
struct node{
int x,y;
node(){}
node(int _x,int _y){
x = _x,y = _y;
}
};
int main(){
int t;
cin>>t;
while(t--){
node a[6];
bool flag[6];
memset(flag,0,sizeof(flag));
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
char h;
cin>>h;
if(h == 'B'){
if(i + j > a[1].x + a[1].y || !flag[1]) a[1] = node(i,j),flag[1] = true;
//越在右下角 i + j 越大
if(i + j < a[2].x + a[2].y || !flag[2]) a[2] = node(i,j),flag[2] = true;
//越在左上角 i + j 越小
if(i - j > a[3].x - a[3].y || !flag[3]) a[3] = node(i,j),flag[3] = true;
//越在右上角 i - j 越大
if(i - j < a[4].x - a[4].y || !flag[4]) a[4] = node(i,j),flag[4] = true;
//越在左下角 i - j 越小
}
}
}
int res = INF;
node ans;
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
int h = 0;
for(int k=1; k<=4; k++){
h = max(h,abs(i - a[k].x) + abs(j - a[k].y));
}
if(h < res){
ans = node(i,j);
res = h;
}
}
}
printf("%d %d\n",ans.x,ans.y);
}
return 0;
}
先去找四个点找到了就每个位置枚举一下,最后输出就好了
【题解】Codeforces Round #798 (Div. 2)的更多相关文章
- [题解] Codeforces Round #549 (Div. 2) B. Nirvana
Codeforces Round #549 (Div. 2) B. Nirvana [题目描述] B. Nirvana time limit per test1 second memory limit ...
- [题解]Codeforces Round #709 (Div. 1, based on Technocup 2021 Final Round) - A. Basic Diplomacy
[题目] A. Basic Diplomacy [描述] Aleksey有n个朋友,有一个m天的假期,每天都需要一个朋友来陪他.给出每天有空的朋友的编号,要求同一个朋友来的天数不能超过m/2上取整.求 ...
- [题解]Codeforces Round #254 (Div. 2) B - DZY Loves Chemistry
链接:http://codeforces.com/contest/445/problem/B 描述:n种药品,m个反应关系,按照一定顺序放进试管中.如果当前放入的药品与试管中的药品要反应,危险系数变为 ...
- [题解]Codeforces Round #254 (Div. 2) A - DZY Loves Chessboard
链接:http://codeforces.com/contest/445/problem/A 描述:一个n*m的棋盘,有一些格子不能放棋子.现在把黑白棋子往上放,要求放满且相邻格子的棋子颜色不同.输出 ...
- 题解——Codeforces Round #508 (Div. 2) T3 (贪心)
贪心的选取最优解 然后相减好 记得要开long long #include <cstdio> #include <algorithm> #include <cstring ...
- 题解——Codeforces Round #508 (Div. 2) T2 (构造)
按照题意构造集合即可 注意无解情况的判断 #include <cstdio> #include <algorithm> #include <cstring> #in ...
- 题解——Codeforces Round #508 (Div. 2) T1 (模拟)
依照题意暴力模拟即可A掉 #include <cstdio> #include <algorithm> #include <cstring> #include &l ...
- Codeforces Round #160 (Div. 1) 题解【ABCD】
Codeforces Round #160 (Div. 1) A - Maxim and Discounts 题意 给你n个折扣,m个物品,每个折扣都可以使用无限次,每次你使用第i个折扣的时候,你必须 ...
- Codeforces Round #383 (Div. 2) 题解【ABCDE】
Codeforces Round #383 (Div. 2) A. Arpa's hard exam and Mehrdad's naive cheat 题意 求1378^n mod 10 题解 直接 ...
随机推荐
- 引入css的方式
---恢复内容开始--- 引入css的样式及link和@import的区别 有3种引入方式 1.内部样式(写在标签内) 2.内联样式 3.外部样式(link @import) 区别: 1.本质区别:l ...
- 《头号玩家》AI电影调研报告(五)
4.VR自由行走跑步机 电影中,它可以让玩家朝任意方向无限奔跑并保持在平台最中央,还可以模拟上台阶和走斜坡的情况.而下面这件VR跑步装备,可以让你在游戏世界穿行自如. 奥地利创意公司Cyberith公 ...
- Python中关于进度条的6个实用技巧
1 简介 费老师我在几年前写过的一篇文章(https://www.cnblogs.com/feffery/p/13392024.html)中,介绍过tqdm这个在当下Python圈子中已然非常流行的进 ...
- DC-1 靶机渗透
DC-1 靶机渗透 *概况*: 下载地址 https://www.vulnhub.com/entry/dc-1,292/ *官方描述:* DC-1 is a purposely built vulne ...
- redis从0-1学习记录(完结)
1. NoSQL(not only sql):不仅仅是数据库,非关系型数据库,关系型数据库是以表格的行列进行存储的,而非关系型数据库是以键值对进行存储,不需要固定的格式.非关系型数据库的特点,方便扩展 ...
- API管理工具介绍
时间都去哪里了 敏捷迭代和团队协作,前后端分离的工作模式几乎是每个互联网公司的常规工作模式. 前后端分离,各自开发的优点很多,其中一项是它只需要提供一个统一的API接口,即可被web,iOS,An ...
- Divide by Zero 2021 and Codeforces Round #714 (Div. 2) B. AND Sequences思维,位运算 难度1400
题目链接: Problem - B - Codeforces 题目 Example input 4 3 1 1 1 5 1 2 3 4 5 5 0 2 0 3 0 4 1 3 5 1 output 6 ...
- 简单了解AndroidManifest.xml文件
AndroidManifest.xml:资源清单文件 <?xml version="1.0" encoding="utf-8"?> <mani ...
- mmdetection训练出现nan
训练出现nan 在使用MMDetection训练模型时,发现打印信息中出现了很多nan.现象是,loss在正常训练下降的过程中,突然变为nan. 梯度裁减 在模型配置中加上grad_clip: opt ...
- python @符号用法的简单理解
一.用作函数修饰符 作用是为现有函数增加额外的功能,常用于插入日志.性能测试.事务处理等等 创建函数修饰符的规则:(1)修饰符是一个函数(2)修饰符取被修饰函数为参数(3)修饰符返回值取代被修饰函数 ...