当前位置: 首页 > news >正文

【403 Error】Atcoder Beginner Contest 403 题解

零、前言

经过 5 5 5 个月 10 10 10 天的分别,本期 ABC 题解又和大家见面啦!

本次要讲解的是 ABC403 的题目,欢迎大家阅读。

本篇题解由庆祝第五次 AK 和重返 1900 1900 1900 分写的。
在这里插入图片描述

一、正文

第 A 题 Odd Position Sum

非常简单,直接模拟即可,把正常遍历的 i++ 换成 i+=2 即可。

当然还有一种写法,每次把 a i × ( i % 2 ) a_i\times(i~\%~2) ai×(i % 2) 加到答案里也行。

注意本题和 G 题有联动。(联动很少见)

代码:

#include <bits/stdc++.h>
using namespace std;
int main(){int n; cin>>n;int ans=0,x;for (int i=1; i<=n; i++){cin>>x;ans+=(i%2)*x;}cout<<ans;
} 

第 B 题 Four Hidden

题意为有一个字符串 x x x,把四个字符改成 ? 后变成 y y y,给定 y y y 和另一个字符串 u u u,求 u u u 能否成为 x x x 的子串。

其实先枚举 y y y 中一个长度为 ∣ u ∣ |u| u 的子串( ∣ ∣ || ∣∣ 表示长度),然后判断这个子串能否和 u u u 相等,判断方法就是这两个字符串的第 i i i 位,要么相等,要么有一个 ?

代码:

#include <bits/stdc++.h>
using namespace std;
int main(){string t,u; cin>>t>>u; int ans=0;for (int i=0; i<=t.size()-u.size(); i++){bool b=1;for (int j=i; j<=i+u.size()-1; j++){if (t[j]==u[j-i]||t[j]=='?') b=b;else b=0;}ans|=b;}cout<<(ans?"Yes":"No");
} 

第 C 题 403 Forbidden

题意说维护一个网站系统,开始用户没有任何权限,每次可以赋予一个用户一种权限或全部权限,还要查询某个用户是否有某种权限。

因为用户数和权限数很多,无法暴力添加,所以需要改进。

其实我们可以定义一种 0 0 0 号权限,表示用户是否拥有全部权限,每次查询 x x x 用户有没有 y y y 权限时只需查 x x x 用户是否有 y y y 0 0 0 号权限即可。

代码:

#include <bits/stdc++.h>
using namespace std;
map <int,int> mp[200010];
int main(){int n,m,q; cin>>n>>m>>q;for (int i=1; i<=q; i++){int op; cin>>op;if (op==1){int x,y; cin>>x>>y;mp[x][y]=1;}else if (op==2){int x; cin>>x; mp[x][0]=1;}else{int x,y; cin>>x>>y;if (mp[x][0]||mp[x][y]) cout<<"Yes\n";else cout<<"No\n";}}
} 

第 D 题 Forbidden Difference

这道题分值 425 425 425 分,所以一定非常非常难

这道题很多人上来卡住了,其实我也是。

这道题看上去虽然非常奇怪,但是可以先做一步转换,求最多留下几个数,再对相同的数合并,这样数被赋予了权值。

其次,我们发现,如果留下 x x x,那么 x + D x+D x+D x − D x-D xD 无法保留,容易 想到可以把数对 D D D 取模分组,问题转化成了一个数列,不能选择相邻的数,问选择的数的最大值是多少(有权值),所以要用 DP 解决。

小 Tip:本题有坑,如果 D = 0 D=0 D=0,答案就是 m a x ( 0 , c n t x − 1 ) max(0,cnt_x-1) max(0,cntx1) 的和, c n t x cnt_x cntx 是数字 x x x 出现的次数。

代码:

#include <bits/stdc++.h>
using namespace std;
int cnt[1000010],dp[1000010][2];
vector <int> vc[1000010];
int main(){int n,m,ans=0; cin>>n>>m;for (int i=1; i<=n; i++){int x; cin>>x; cnt[x]++;}if (m==0){int cntt=0;for (int i=0; i<=1e6; i++){cntt+=max(0,cnt[i]-1);}cout<<cntt;return 0;}for (int i=0; i<=1e6; i++){vc[i%m].push_back(cnt[i]);}for (int i=0; i<=m; i++){for (int j=1; j<=vc[i].size(); j++){dp[j][0]=max(dp[j-1][0],dp[j-1][1]);dp[j][1]=dp[j-1][0]+vc[i][j-1];}ans+=max(dp[vc[i].size()][0],dp[vc[i].size()][1]);}cout<<n-ans;
} 

第 E 题 Forbidden Prefix

题意是有两个可重字符串集 X , Y X,Y X,Y,每次向其中一个集合里添加字符串,添加之后求有多少个字符串属于 Y Y Y 却没有属于 X X X 的前缀。

字符串,前缀,可重集,这不是 Trie 又是什么?我们维护一个 Trie。

我们先解决插入问题,因为插入非常简单,我们找到字符串所在的路径然后路径加一即可。如果在加入过程中遇到删除标记就停止并把之前的加都减回去。

如果我们要删除呢?也是找到所在路径,如果路径上有删除标记就结束(无用操作),接下来我们假设走到了 i d id id,我们把 i d id id 的标记清零,打上标记,然后再把路径上所有的点都减去这个标记即可。

小 Tip 如果插入,如果结束 i d id id 有删除标记也要回删,所以注意代码顺序。

代码:

#include <bits/stdc++.h>
using namespace std;
int tr[500010][26],shan[500010],cnt[500010],tot=1;
void ins(string s){int id=1;for (auto x:s){if (!tr[id][x-'a']) tr[id][x-'a']=++tot;cnt[id]++; id=tr[id][x-'a'];if (shan[id]){ id=1;for (auto x:s){if (shan[id]) break;cnt[id]--; id=tr[id][x-'a'];}return ;}}cnt[id]++;
}
void del(string s){int id=1;for (auto x:s){if (shan[id]) return ;if (!tr[id][x-'a']) tr[id][x-'a']=++tot;id=tr[id][x-'a'];}int tmp=cnt[id]; shan[id]=1; cnt[id]=0; id=1; for (auto x:s){cnt[id]-=tmp;id=tr[id][x-'a'];}
}int main(){ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);int t; cin>>t;while (t--){int op; string s; cin>>op>>s;if (op==2) ins(s);else del(s);cout<<cnt[1]<<"\n";}
} 

第 F 题 Shortest One Formula

题意为求一个最段的,由 + × ( ) 1 +\times(~)~1 +×( ) 1 组成的表达式,使得结果为 n n n

考虑 DP,设 d p i dp_i dpi n = i n=i n=i 时的表达式长度,但是这样有优先级的问题。

可以考虑升维, d p i , 0 / 1 dp_{i,0/1} dpi,0/1 n = i n=i n=i 时,最外层计算是 + + + × \times × 组成的最短表达式长度,这样我们就可以转移了。

如果用加: d p i , 0 = m i n ( d p j , 0 / 1 + d p i − j , 0 / 1 ) dp_{i,0}=min(dp_{j,0/1}+dp_{i-j,0/1}) dpi,0=min(dpj,0/1+dpij,0/1)

如果用乘(和括号) d p i , 1 = m i n ( d p j , l + d p i / j , r + 2 l + 2 r ) dp_{i,1}=min(dp_{j,l}+dp_{i/j,r}+2l+2r) dpi,1=min(dpj,l+dpi/j,r+2l+2r)

可是要输出答案,这可有点问题,到底怎么可以记录答案呢,实际上可以记 p r e pre pre 数组,表示从哪里转移而来,具体记录方式见代码,然后我们可以用类似递归的方式输出答案了。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
int dp[2010][2],pre[2010][2][4];
void out(int id,int sta){if (!pre[id][sta][0]){cout<<id;return ;}if (sta==1){out(pre[id][sta][0],pre[id][sta][1]);cout<<"+";out(pre[id][sta][2],pre[id][sta][3]);}else{if (pre[id][sta][1]) cout<<"(";out(pre[id][sta][0],pre[id][sta][1]);if (pre[id][sta][1]) cout<<")";cout<<"*";if (pre[id][sta][3]) cout<<"(";out(pre[id][sta][2],pre[id][sta][3]);if (pre[id][sta][3]) cout<<")";}
}
signed main(){ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);int n; cin>>n;memset(dp,0x3f,sizeof(dp));dp[1][0]=1;dp[11][0]=2;dp[111][0]=3;dp[1111][0]=4;for (int i=2; i<=2000; i++){for (int j=1; j<=i; j++){if (i%j==0){for (int l=0; l<2; l++){for (int r=0; r<2; r++){int val=dp[j][l]+l*2+dp[i/j][r]+r*2+1;if (val<dp[i][0]){dp[i][0]=val;pre[i][0][0]=j;pre[i][0][1]=l;pre[i][0][2]=i/j;pre[i][0][3]=r;}}}}}for (int j=1; j<i; j++){for (int l=0; l<2; l++){for (int r=0; r<2; r++){int val=dp[j][l]+dp[i-j][r]+1;if (val<dp[i][1]){dp[i][1]=val;pre[i][1][0]=j;pre[i][1][1]=l;pre[i][1][2]=i-j;pre[i][1][3]=r;}}}}} for (int i=n; i<=n; i++){if (dp[i][0]<dp[i][1]) out(i,0);else out(i,1);}
} 

附赠一份 1 1 1 2000 2000 2000 所有 n n n 的答案表以供大家研究。

第 G 题 Odd Position Sum Query

感谢大家坚持读到最后,还记得讲 A 时说本题与 A 联动,那么现在就来看一看这道题。

强制在线维护一个数组,支持加入元素和查询排名为奇数的数的和。

容易 想到动态开点值域线段树,我们只需要弄明白到底记录什么信息就可以了。

首先,一定要记录答案,但是合并时,左侧节点的大小若是奇数,那么答案并不是简单的求和了,会发现右侧的偶数位会变成奇数位,所以还要记录大小和偶数位答案,有了这三个信息,合并也就很简单了。

小 Tip 注意有重复数字。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mid (l+r>>1)
struct node{int sz,jans,oans;
}tr[12000010];
int tot=1,ls[12000010],rs[12000010];
node merge(node a,node b){node ans;ans.sz=a.sz+b.sz;ans.jans=a.jans+(a.sz%2?b.oans:b.jans);ans.oans=a.oans+(a.sz%2?b.jans:b.oans);return ans;
}
void update(int id,int l,int r,int qid,int val){if (l==r){tr[id].sz++;if (tr[id].sz%2==1) tr[id].jans+=val;else tr[id].oans+=val;return ;}if (qid<=mid){if (!ls[id]) ls[id]=++tot;update(ls[id],l,mid,qid,val);}else{if (!rs[id]) rs[id]=++tot;update(rs[id],mid+1,r,qid,val);}tr[id]=merge(tr[ls[id]],tr[rs[id]]);
}signed main(){ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);int t,pre=0; cin>>t;for (int i=1; i<=t; i++){int x; cin>>x;x=(x+pre)%1000000000+1;update(1,1,1e9,x,x);pre=tr[1].jans;cout<<pre<<"\n";}
} 

二、一些有用的链接

Atcoder Beginner Contest 403

Atcoder 难度评级

出题组(招人中)

感谢大家的阅读,我们下期再见

相关文章:

  • Redo log,Undo log和binlog
  • 系统思考提升培训效能
  • 培养一个输出型的爱好
  • 【Git】项目多个分支开发、维护与优化处理 ing
  • miniconda在ARM64位芯片上面的安装
  • Windows应用-屏幕截图
  • 解决 shadui组件库Popover 点击后会消失
  • 【蓝桥杯省赛真题58】Scratch画台扇 蓝桥杯scratch图形化编程 中小学生蓝桥杯省赛真题讲解
  • 人工智能与机器学习:Python从零实现K-Means 算法
  • frp内网穿透的基础使用
  • 疫苗接种体系进入“全生命周期”时代:公共卫生治理再提速
  • Lustre/Scade 语言时序算子与形式化验证的联系
  • 多元函数微分之传统方法和全微分法
  • 电子监管码预检剔除装置提示盒尺寸过短
  • php 需要学会哪些技术栈,掌握哪些框架
  • 架构风格对比
  • new的使用
  • 泰山派常用命令
  • ICH CTD中ISS的关键内容与作用
  • params query传参差异解析及openinstall跨平台应用
  • 民生银行一季度净利127.42亿降逾5%,营收增7.41%
  • 美国通过《删除法案》:打击未经同意发布他人私密图像,包括“深度伪造”
  • 老凤祥一季度净利减少两成,去年珠宝首饰营收下滑19%
  • 黄永年:说狄仁杰的奏毁淫祠
  • “中国游”带火“中国购”,“即买即退”让外国游客购物更丝滑
  • 加拿大驾车撞人事件遇难人数升到11人