2024.3.1|牛客周赛 Round 36
A.小红的数位删除
B.小红的小红矩阵构造
C.小红的白色字符串
D.小红走矩阵
E.小红的小红走矩阵
F.小红的好子串询问
心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。
小红的数位删除
题目:
小红拿到了一个正整数,她希望删除该正整数的最后三位,你能帮帮她吗?
输入描述:
一个正整数n
1000≤n≤109
输出描述:
删除最后三位后形成的正整数。
示例1
输入
12345
输出
12
注意:
A题是签到题。用.substr()更快。
实践代码:
void solve(){
string s;cin>>s;
int n;cin>>n;
cout<<s.substr(0,s.length()-3);
}
优化:
void solve()
{
int t,n;cin>>t>>n;
while(t--){
int c = 0;
for (int i = 1; i <= n; i++){
int x;cin>>x;
if(x)c++;
else c--;
}
cout<<abs(c)<<endl;
}
}
小红的小红矩阵构造
题目:
小红拿到了一道题,题目设定如下:
https://ac.nowcoder.com/acm/problem/268977
小红希望你构造一个n行m列的矩阵,满足所有元素之和恰好等于x,且每行、每列的异或和全部相等。你能帮帮她吗?
【样例输入】
4 4 4
【样例输出】
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
小红希望你判断她的输出是否符合要求,你能帮帮她吗?
输入描述:
第一行输入三个正整数n,m,x,代表小红做这道题的输入部分。
接下来的n行,每行输入m个非负整数,代表小红构造的矩阵。
1≤n,m≤100
0≤x≤109
保证x是偶数。小红输出的元素保证不超过109
输出描述:
如果小红成功通过了本题,则输出"accepted"。否则输出"wrong answer"
示例1
输入
4 4 14
1 2 3 0
1 2 2 1
0 0 1 1
0 0 0 0
输出
accepted
示例2
输入
4 4 4
1 1 1 1
0 0 0 0
0 0 0 0
0 0 0 0
输出
wrong answer
示例3
输入
4 4 4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
输出
wrong answer
注意:
B题首先要了解异或,且本题数据范围不大,可以暴力。
首先判断矩阵元素总和,其次判断每行和每列的异或和是否相等。
补充:
异或:异或,是一个数学运算符,英文为exclusive OR,缩写为xor,应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。其运算法则为:a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位。
参与运算的两个值,如果两个相应bit位相同,则结果为0,否则为1。
即:
0^0 = 0,
1^0 = 1,
0^1 = 1,
1^1 = 0
求a,b,c,d的异或和:a ^b ^c ^d
实践代码:
void solve(){
int n,m,x,sum=0;cin>>n>>m>>x;
vector<vector<int>> a(n+1,vector<int>(m+1));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[i][j];
sum+=a[i][j];
}
}
if(sum!=x) {cout<<"wrong answer";return;}
int flag=0;//记录第一行的异或和
for(int i=0;i<n;i++){//按行判断
int ans=0;
for(int j=0;j<m;j++){
ans^=a[i][j];
}
if(i==0) flag=ans;//将第一行的元素异或和存在flag中,用于后续比较
else if(ans!=flag) {cout<<"wrong answer";return;}
}
for(int j=0;j<m;j++){
int ans=0;
for(int i=0;i<n;i++){
ans^=a[i][j];
}
if(ans!=flag) {cout<<"wrong answer";return;}
}
cout<<"accepted";return;
}
小红的白色字符串
题目:
小红拿到了一个字符串,她准备将一些字母变成白色,变成白色的字母看上去就和空格一样,这样字符串就变成了一些单词。
现在小红希望,每个单词都满足以下两种情况中的一种:
1.开头第一个大写,其余为小写(长度为 1 的大写字母也是合法的)。
2.所有字符全部是小写。
小红想知道,最少需要将多少字母变成白色?
输入描述:
一个仅包含大小写字母的字符串。
字符串长度不超过200000
输出描述:
将字母变成白色的最小数量。
示例1
输入
aDRRanko
输出
2
说明
将第二个和第三个字母变成白色即可,字符串变成 “a Ranko”
注意:
C题,这道题有两种情况且让满足其一,我们可以清楚大写字母属于特殊情况要加以判断,且第一个字母不论大小写均合法,所以直接从第二个字母开始判断大小写,如果大写的话就涂成空白。
特殊情况:注意如果此时此位置为大写字母且前一个位置已涂成空白(说明此时此处为这一字串的开头字母,而开头字母大写满足第一个要求),我们不能再涂。
实践代码:
void solve(){
string s;cin>>s;
int cnt=0;
for(int i=1;i<s.length();i++){
if(s[i]>='A'&&s[i]<='Z'&&s[i-1]!=' '){
s[i]=' ';
cnt++;
}
}
cout<<cnt;
}
小红走矩阵
题目:
小红来到了一个n∗m的矩阵,她初始站在左上角,每次行走可以按“上下左右”中的一个方向走一步,但必须走到和当前格子不同的字符,也不能走到矩阵外。
小红想知道,从左上角走到右下角最少需要走多少步?
输入描述:
第一行输入两个正整数n,m,用空格隔开。代表矩阵的行数和列数。
接下来的n行,每行输入一个长度为m的、仅由小写字母组成的字符串,用来表示矩阵。
1≤n,m≤1000
输出描述:
如果无法到达右下角,则输出-1。
否则输出一个整数,代表行走的最小步数。
示例1
输入
3 4
abbc
accd
bcee
输出
9
说明
注意:
D题是一个典型的bfs+最短路径(二维数组存储)。
实践代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 1010;
char a[N][N];
bool vis[N][N];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,1,-1};
int d[N][N];//更新最短路径
struct node{
int x,y;
};
void bfs(int n,int m){
queue<node> q;
q.push({0,0});
vis[0][0]=true;
while(!q.empty()){
node tmp=q.front();
q.pop();
for(int i=0;i<4;i++){
int tx=tmp.x+dx[i],ty=tmp.y+dy[i];
if(tx<0||tx>n||ty<0||ty>m||a[tx][ty]==a[tmp.x][tmp.y]||vis[tx][ty])
continue;
d[tx][ty]=d[tmp.x][tmp.y]+1;//往前走一步就在上一步的基础上+1
q.push({tx,ty});//将当前点位存起来
vis[tx][ty]=true;
}
}
}
void solve(){
int n,m;cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[i][j];
}
}
bfs(n-1,m-1);
if(vis[n-1][m-1]) cout<<d[n-1][m-1];
else cout<<-1;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}
小红的小红走矩阵
题目:
小红在出题“小红的走矩阵”时,在数据生成的环节发现了一些问题。
如果直接随机生成一个矩阵,那么结果大概率可以直接输出 n+m−2。
如果直接生成极限数据,那么结果也将是跑完整个矩阵,因此可以直接输出 n∗m−1。
为了避免以上的情况骗到分,于是小红想到了另一个方案:先随机生成一个从 (1,1) 到 (n,m) 的路径,再将路径以外的矩阵的部分全部填成同一个字符。这样一来看似数据确实强,答案并不固定,但实际上也有非常明显的弊端,因为矩阵中大部分都是同一个字符,且这个字符在路径之外,因此选手可以通过“矩阵中是否存在绝对众数”来判断数据是否是这样的数据。
因此小红现在在数据生成的问题上犯了难,她希望小苯帮她来完成本题数据的生成,即请你来代替小苯构造出本题较强的数据。
使得所有的数据都能满足以下的所有条件:
- 直接输出 n+m−2 会返回答案错误,换句话说,“小红走矩阵”这题的正确代码运行后的结果不是 n+m−2。
- 直接输出 n∗m−1 也会返回答案错误,换句话说,“小红走矩阵”这题的正确代码运行后的结果不是 n∗m−1。
- 生成的矩阵中,不存在“绝对众数”。(即,没有任何字符的出现次数大于 n∗m/2 向上取整)
- 生成的矩阵必须能从 (1,1) 走到 (n,m),换句话说,“小红走矩阵”这题的正确代码运行后的结果不是 −1。
输入描述:
输入包含一行两个正整数 n,m (3≤n,m≤103 ),分别表示需要生成的矩阵的大小,以空格分割。
输出描述:
输出包含一个 n 行 m 列 的字符矩阵,满足题目所述的要求,且仅由小写英文字母构成。(如果有多种方案,输出任意即可)
示例1
输入
3 4
输出
abbc
accd
bcee
说明
备注
注意:
E题为构造题。
第一个条件:不能n+m-2即不能直来直去的构造。
第二个条件:n*m-1不很好构造,可以忽略。
第三个条件:不存在“绝对众数”,避免构造出来一种路径后,剩余其他都用同一个字母。
第四个条件:必须有一条最短路径能从左上角到达右下角。
所以我们可以这样构造,即从第一行直走到头,再从最后一列直走到倒数第2个点,之后左转走一个点,向下走一个点,再向右走到终点(避免第2个条件不成立,走一个S型)
实践代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int a[1010][1010];
void solve(){
int n,m;cin>>n>>m;
/*前两行构造abab...使之只能向右走到头*/
for(int i=0;i<2;i++){//前两行
for(int j=0;j<m;j++) a[i][j]=j%2+1;//abab...构造
}
/*使第二行之后的每行最后两个位置元素相等,使之走到头后能向下走*/
for(int i=1;i<m;i++){//第二行
a[i][m-1]=a[i][m-2]=(i+m+1)%2+1;//根据行数i和m的奇偶性来赋值,如果m为奇数,则后面两个表达式需是b,反之亦然
}
//需要走s型,强制改一下最后两行的最后两个所经路径位置的元素
a[n-1][m-2]=4;//终点左边的位置
a[n-2][m-1]=3;//终点上边的位置
a[n-1][m-1]=3;//终点位置,与上边元素相等使之不能一步到达
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(!a[i][j]) a[i][j]=rand()%26+1;//剩下的位置随机数,使之避免“绝对众数”
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cout<<(char)(a[i][j]-1+'a');
}
cout<<endl;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}
小红的好子串询问
题目:
小红定义一个字符串是“好串”,当且仅当该字符串不包含长度不小于 2 的回文子串。
现在小红拿到了一个仅包含"red"三种字符的字符串,她有如下两个操作:
· 输入 1 x chr:将第x个字符修改为chr
· 输入 2 l r:询问第l个字符到第r个字符的子串再修改最少多少个字符可以变成好串(每次修改后也必须是"red"三种字符中的一种)。
你能帮帮她吗
输入描述:
第一行输入两个正整数
n,q,代表字符串长度、操作次数。
第二行输入一个仅包含长度为n的、"red"三种字符的字符串。
接下来的q行,每行输入一个操作1 x chr或者2 l r,含义如上所述。
1 ≤ n , q ≤ 105
1 ≤ x ≤ n
1 ≤ l ≤ r ≤ n
chr∈ ‘r’,‘e’,‘d’
保证至少有一次操作 2。
本题有5%的数据满足:1≤n,q≤5
有25%的数据满足:1≤n,q≤2000
另外有20%的数据满足:所有操作均为操作2。
输出描述:
对于每个操作 2,输出一个整数表示答案,即子串变成好串的最小修改次数。
示例1
输入
5 3
deder
2 2 4
1 3 r
2 1 3
输出
1
0
说明
第一次询问的子串是"ede",将第一个或者第三个字符修改为’r’可以变成好串。
第二次询问的子串是"der",是好串。
注意:
F题用树状数组和前缀和。
实践代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
/*长度为n的回文串<=>一定包含一个长度为n-2的回文子串
逆否命题:不包含一个长度为n-2的回文子串<=>不是一个长度为n的回文串
*/
string str[6] = {"red","rde","edr","erd","der","dre"};
int t[404040][6];//树状数组
void add(int x,int id,int p){
for(int i=x;i<=2e5;i+=i&-i){
t[i][id]+=p;
}
}
int ask(int x,int id){
int res=0;
for(int i=x;i>0;i-=i&-i){
res+=t[i][id];
}
return res;
}
int ask(int l,int r,int id){
int res=ask(r,id);
if(l>1) res-=ask(l-1,id);
return res;
}
void solve(){
int n,q;cin>>n>>q;
string s;cin>>s;
s=' '+s;
for(int i=1;i<=n;i++){
for(int j=0;j<6;j++){
if(s[i]!=str[j][i%3]) add(i,j,1);
}
}
while(q--){
int op;cin>>op;
if(op==1){
int x;char c;cin>>x>>c;
for(int i=0;i<6;i++){
if(s[x]!=str[i][x%3]) add(x,i,-1);
if(c!=str[i][x%3]) add(x,i,1);
}
s[x]=c;
}
else{
int l,r;
cin>>l>>r;
int res=1e9;
for(int i=0;i<6;i++){
res=min(res,ask(l,r,i));
}
cout<<res<<endl;
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}
心有猛虎,细嗅蔷薇。再见了朋友~