比较菜只有 A ~ E

A.Erasing Zeroes

题目描述:

原题面

题目分析:

使得所有的 \(1\) 连续也就是所有的 \(1\) 中间的 \(0\) 全部去掉,也就是可以理解为第一个 \(1\) 到最后一个 \(1\) 中间的 \(0\) 全部去掉,也就是它们之间 \(0\) 的个数,那么就顺序、逆序扫一遍就出来了。

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
string s;
cin>>s;
int l = -1,r = -1;
for(int i=0; i<s.size(); i++){
if(s[i] == '1'){
l = i;
break;
}
}
for(int i=s.size(); i>=0; i--){
if(s[i] == '1'){
r = i;
break;
}
}
if(l == -1){
printf("0\n");
continue;
}
int ans = 0;
for(int i=l; i<=r; i++){
if(s[i] == '0'){
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}

B.National Project

题目描述:

原题面

题目分析:

题目要求是至少有一半是在好天气中修的,那么我们就考虑如果好天气都修那么修到哪一天可以满足这个条件。

我们可以将每 \(g\) 天视为一轮,那么总共就有 \(\lfloor\dfrac{\lceil \dfrac{n}{2} \rceil}{g}\rfloor\) 个完整的轮数,以及多出来的 \(\lceil \dfrac{n}{2} \rceil\% g\) 天。这一轮既有好天气也有坏天气,所以最后得到在哪一天可以满足条件时要算上坏天气的天数。如果多出来的天数不为 \(0\),那么就意味着这么多轮坏天气都需要经过,而如果多出来的天数为 \(0\),那么意味着经过坏天气的轮数是我们求出来的轮数减一,因为最后一轮不需要经过坏天气。

所以最后在哪一天可以满足条件的答案就是:(令 \(x = \lfloor\dfrac{\lceil \dfrac{n}{2} \rceil}{g}\rfloor\))

(1)若有多余的天数:\(x \times (g+b) + \lceil \dfrac{n}{2} \rceil\% g\)

(2)若没有多余的天数:\((x-1)\times b + x \times g\)

需要注意的是这几天只是满足好天气一半的条件,不一定可以全部修完,所以需要与 \(n\) 取 \(\max\)

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int main(){
long long t;
cin>>t;
while(t--){
long long n,g,b;
cin>>n>>g>>b;
long long x = (n+1)/2/g;
if(((n+1)/2)%g == 0){
printf("%lld\n",max((x-1) * b + x * g,n));
}
else{
printf("%lld\n",max(x * (g+b) + ((n+1)/2) % g,n));
}
}
return 0;
}

一个优美的技巧:\(\lceil \dfrac{n}{2} \rceil\) 可以写为: \(\lfloor \dfrac{n+1}{2} \rfloor\)

C.Perfect Keyboard

题目描述:

原题面

题目分析:

这种题很显然要先考虑转化为图上的问题。

很显然我们可以在 \(S\) 中相邻的两个字符之间连边,表示这两个字符必须相邻。

判断无解的条件也很明显了:

(1)某个点的度数大于等于 \(3\),因为一条连边表示一个相邻关系,不可能同时与三个字符相邻。

(2)出现大于等于 \(3\) 的环,这个也是相当显然的自己手推一下就知道不可能

那么剩下的就是一条条的链了,那么就按顺序扫过这一条链,到了某个节点就输出这个节点代表的字母就好了,为了避免重复输出应该记一个数组表示这个字母有没有输出过。也需要注意一点:要从度数为 \(1\) 的点开始扫,因为度数为 \(2\) 意味着一种中间的关系,显然只能一边边地输出无法从中间扩展。

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 50;
int head[MAXN],cnt,du[MAXN],edge[MAXN][MAXN];
bool vis[MAXN],flag,use[MAXN];
void dfs(int now,int from){
vis[now] = true;
for(int i=0; i<26; i++){
if(i == from || !edge[now][i] || now == i) continue;
if(vis[i]){
flag = true;
return;
}
dfs(i,now);
}
}
void get_ans(int now,int from){
if(!vis[now])
cout<<char(now + 'a');
vis[now] = true;
for(int i=0; i<26; i++){
if(vis[i] || i == from || !edge[now][i] || now == i) continue;
get_ans(i,now);
}
}
int main(){
int t;
cin>>t;
while(t--){
memset(edge,0,sizeof(edge));
memset(vis,false,sizeof(vis));
memset(use,false,sizeof(use));
memset(du,0,sizeof(du));
cnt = 0;
flag = false;
int mx = 0;
string s;
cin>>s;
for(int i=0; i<s.size() - 1; i++){
if(!edge[s[i]-'a'][s[i+1]-'a']){
du[s[i]-'a']++;
du[s[i+1]-'a']++;
mx = max(mx,max(du[s[i]-'a'],du[s[i+1]-'a']));
}
edge[s[i]-'a'][s[i+1]-'a'] = true;
edge[s[i+1]-'a'][s[i]-'a'] = true;
use[s[i] - 'a'] = true;
use[s[i+1] - 'a'] = true;
}
for(int i=0; i<26; i++){
if(!vis[i]){
dfs(i,i);
}
if(flag)
break;
}
if(flag || mx >= 3){
printf("NO\n");
}
else{
printf("YES\n");
memset(vis,false,sizeof(vis));
for(int i=0; i<26; i++){
if(du[i] == 1)
get_ans(i,i);
}
for(int i=0; i<26; i++){
if(!vis[i])
cout<<char(i + 'a');
}
printf("\n");
}
}
return 0;
}

因为可能含有大量的重边而且点的数量很少所以可以考虑使用邻接矩阵。

输出的时候也要注意可能有的点没有出现那么就在最后输出这些没有出现的点。

所谓有大小大于等于三的环也可以理解为无向图上的有环,也就是如果从当前节点可以到达一个曾经访问过但不是其父亲的点那么就意味着有环。

D.Fill The Bag

题目描述:

原题面

题目分析:

考虑物品大小都是 \(2\) 的非负整数次幂也就可以联想到将 \(n\) 二进制拆分,因为这些物品的顺序不影响答案所以就考虑将他们存放到 \(cnt\) 数组中去。

考虑如果 \(n\) 的当前位我们数组中有那么直接减就好了,很显然这样做最优。而如果没有那么就要考虑是用比当前位小的那些数加和凑出这个位还是从比当前位更大的位拆。

那么这样就意味着我们要维护一个 \(sum\) 代表比当前位小的那些数的和,如果这个值大于当前位的值显然就扫一遍比它小的位然后减去这些位的值直到减为 \(0\) 就好。

而如果用比它小的数凑不出来当前位那么就找到比当前位大的最小的有值一位,然后从这一位一直拆,拆到当前位然后减掉就好了。

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long MAXN = 3e5+5;
long long cnt[MAXN];
int main(){
long long t;
cin>>t;
while(t--){
memset(cnt,0,sizeof(cnt));
long long flag = 0;
long long n,m;
long long mx = 0;
cin>>n>>m;
for(long long i=1; i<=m; i++){
long long a;
cin>>a;
cnt[int(log2(a))]++;
mx = max(mx,a);
flag += a;
}
if(flag < n){
printf("-1\n");
continue;
}
long long sum = 0;
long long now = 0;
long long ans = 0;
while(n){
if(n & 1){
if(cnt[now]){
cnt[now]--;
}
else if(sum >= (1<<now)){ //用低位补
long long res = (1<<now);
for(long long i=now-1; i>=0 && res; i--){
if(cnt[i] * (1<<i) <= res){
sum -= cnt[i] * (1<<i);
res -= cnt[i] * (1<<i);
cnt[i] = 0;
}
else{
while(cnt[i] && (1<<i) <= res){
res -= (1<<i);
sum -= (1<<i);
cnt[i]--;
}
}
}
}
else{ //找到高位拆
for(long long j = now+1; j<=mx; j++){
if(cnt[j]){
for(long long k=j; k>now; k--){
cnt[k-1] += 2;
cnt[k]--;
ans++;
}
break;
}
}
cnt[now]--;
}
}
n>>=1;
sum += cnt[now] * (1<<now);
now++;
}
cout<<ans<<endl;
}
return 0;
}

注意一开始先判断有无解,也就是所有数加起来能不能大于等于 \(n\),若能大于等于显然有解。

E.Erase Subsequences

题目描述:

原题面

题目分析:

我们很明显可以想出来一步:枚举 \(t\) 在哪里拆开,然后将 \(t\) 转化为 \(t1+t2\),再判断 \(s\) 中能不能拆出 \(t1,t2\) 就好了。

那么问题就转化为了 \(s\) 中能不能拆出来的问题了。发现可能需要 \(dp\) 求解。

很显然的状态是:\(dp[i][j][k]\) 表示 \(s\) 的前 \(i\) 位能不能拆出 \(t1\) 的前 \(j\) 位和 \(t2\) 的前 \(k\) 位,因为状态量过大我们就考虑优化这个状态。

也就是将状态改写为:\(dp[i][j]\) 表示 \(s\) 的前 \(i\) 位拆出 \(t1\) 的前 \(j\) 位最多再拆出 \(t2\) 的前多少位。这样当 \(dp[size_s][size_{t1}] = size_{t2}\) 时也就意味着可以拆出。

那么就考虑转移,转移也就是做决策,这里的决策显然就是 \(s\) 的当前位应该放到 \(t1\) 的后面还是 \(t2\) 的后面还是都不放,这样也能保证是不相交的子序列。

(1)当 \(s[i+1] = t1[j+1]\) 时,\(dp[i][j] -> dp[i+1][j+1]\)

(2)当 \(s[i+1] = t2[f[i][j] + 1]\) 时,\(dp[i][j] + 1 -> dp[i+1][j]\)

(3)任何情况下,\(dp[i][j] -> dp[i+1][j]\),这个也十分显然

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 805;
int f[MAXN][MAXN];
bool check(string s,string t1,string t2){
memset(f,-1,sizeof(f));
f[0][0] = 0;
s = '0' + s;
t1 = '0' + t1;
t2 = '0' + t2;
for(int i=0; i<s.size(); i++){
for(int j=0; j<t1.size(); j++){
if(f[i][j] == -1) continue;
f[i+1][j] = max(f[i+1][j],f[i][j]);
if(s[i+1] == t1[j+1]) f[i+1][j+1] = max(f[i+1][j+1],f[i][j]);
if(s[i+1] == t2[f[i][j] + 1]) f[i+1][j] = max(f[i+1][j],f[i][j] + 1);
}
}
if(f[s.size()-1][t1.size()-1] == t2.size()-1)
return true;
return false;
}
bool solve(string s,string t){
for(int i=0; i<=t.size(); i++){
string t1,t2;
for(int j=0; j<i; j++){
t1 = t1 + t[j];
}
for(int j=i; j<t.size(); j++){
t2 = t2 + t[j];
}
if(check(s,t1,t2))
return true;
}
return false;
}
int main(){
int n;
cin>>n;
while(n--){
string s,t;
cin>>s>>t;
if(solve(s,t)){
printf("YES\n");
}
else{
printf("NO\n");
}
}
return 0;
}

我们会发现对于 \(dp\) 的边界、初值不好设置,那么就将所有的字符串前面加上一位那就好转移了。

【题解】Educational Codeforces Round 82的更多相关文章

  1. Educational Codeforces Round 82 (Rated for Div. 2) A-E代码(暂无记录题解)

    A. Erasing Zeroes (模拟) #include<bits/stdc++.h> using namespace std; typedef long long ll; ; in ...

  2. Educational Codeforces Round 82 (Rated for Div. 2)

    题外话 开始没看懂D题意跳了,发现F题难写又跳回来了.. 语文好差,码力好差 A 判第一个\(1\)跟最后一个\(1\)中\(0\)的个数即可 B 乘乘除除就完事了 C 用并查集判一下联通,每个联通块 ...

  3. Educational Codeforces Round 82 (Rated for Div. 2)E(DP,序列自动机)

    #define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h> using namespace std; ],t[]; int n,m; ][]; ...

  4. Educational Codeforces Round 82 (Rated for Div. 2)D(模拟)

    从低位到高位枚举,当前位没有就去高位找到有的将其一步步拆分,当前位多余的合并到更高一位 #define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h&g ...

  5. Educational Codeforces Round 82 C. Perfect Keyboard

    Polycarp wants to assemble his own keyboard. Layouts with multiple rows are too complicated for him ...

  6. Educational Codeforces Round 82 B. National Project

    Your company was appointed to lay new asphalt on the highway of length nn. You know that every day y ...

  7. Educational Codeforces Round 82 A. Erasing Zeroes

    You are given a string ss. Each character is either 0 or 1. You want all 1's in the string to form a ...

  8. [CF百场计划]#3 Educational Codeforces Round 82 (Rated for Div. 2)

    A. Erasing Zeroes Description You are given a string \(s\). Each character is either 0 or 1. You wan ...

  9. Educational Codeforces Round 63 (Rated for Div. 2) 题解

    Educational Codeforces Round 63 (Rated for Div. 2)题解 题目链接 A. Reverse a Substring 给出一个字符串,现在可以对这个字符串进 ...

随机推荐

  1. Java语言学习day32--8月07日

    ###07正则表达式邮箱地址验证 * A: 正则表达式邮箱地址验证 * a: 案例代码 public class RegexDemo2 { public static void main(String ...

  2. Spring的3级缓存和循环引用的理解

    此处是我自己的一个理解,防止以后忘记,如若那个地方理解不对,欢迎指出. 一.背景 在我们写代码的过程中一般会使用 @Autowired 来注入另外的一个对象,但有些时候发生了 循环依赖,但是我们的代码 ...

  3. Kernel pwn 基础教程之 Heap Overflow

    一.前言 在如今的CTF比赛大环境下,掌握glibc堆内存分配已经成为了大家的必修课程.然而在内核态中,堆内存的分配策略发生了变化.笔者会在介绍内核堆利用方式之前先简单的介绍一下自己了解的内核内存分配 ...

  4. 技术管理进阶——一线Leader与大Leader的差异是什么?

    原创不易,求分享.求一键三连 ​ 前几年,因为下属的一些骚操作,Leader对我产生了误会,这导致了一些"不信任",这种不信任在短时间会让人丧失安全感,很多人会因此患得患失. 这种 ...

  5. WIN10 使用POWERSHELL 设置单应用KIOSK模式(win10家庭版或企业版)

    win10 使用PowerShell 设置单应用kiosk模式 win10 家版或企业版PowerShellshell 启动器 v1Autologon.exe 注意事项 win10 家庭版或企业版. ...

  6. 十分钟快速了解《你不知道的 JavaScript》(上卷)

    最近刚刚看完了<你不知道的 JavaScript>上卷,对 JavaScript 有了更进一步的了解. <你不知道的 JavaScript>上卷由两部分组成,第一部分是< ...

  7. webpack与vite的对比

    vite与webpack的打包原理: vite: 基于游览器原生ES Module,利用游览器解析import,服务器端按需编译返回 webpack: 逐级递归识别依赖,构建依赖图谱->转化AS ...

  8. MySQL UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中

    union 会删除重复数据 union all 不会删除重复数据 select * from ( select *,'a' as kind from tablea where name is not ...

  9. 【工具-Nginx】从入门安装到高可用集群搭建

    文章已收录至https://lichong.work,转载请注明原文链接. ps:欢迎关注公众号"Fun肆编程"或添加我的私人微信交流经验 一.Nginx安装配置及常用命令 1.环 ...

  10. Java类包

    学习内容:Java类包 一.Java类包 1.一个完整的类名需要包名和类名的组合,每一个类都隶属于一个包. 例:完整类名--java.sql.Date 2.同一个包中类相互访问时可以不指明包名. 3. ...