前言:
这次周赛的DEF题,F题相当精彩,乍一看,要经过分析,才能看出是线段树
D题:
题干:
https://ac.nowcoder.com/acm/contest/77273/D
小红来到了一个n∗mn*mn∗m的矩阵,她初始站在左上角,每次行走可以按“上下左右”中的一个方向走一步,但必须走到和当前格子不同的字符,也不能走到矩阵外。
小红想知道,从左上角走到右下角最少需要走多少步?
思路:
非常典型的BFS模板题,这里只给出两种常见的BFS模板,供参考
模板一:(stl写法)
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int d[][]//*路径记录器;
int vis[][];//*探索是否访问
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};//*四方向搜索
//*int nx[] = {1,-1,0,0,0,0}; int ny[] = {0,0,1,-1,0,0}; int nz[] = {0,0,0,0,1,-1}; 六方向*//
struct node{ //*标记当前坐标
int x;
int y;
};
bool check(int x,int y) //*约束条件
{
/*****/
}
void bfs(int flag,int x,int y)
{
int f=0,r=0,x1,y1;//*队列
node p,temp;
queue<node>q;
p.x=x;
p.y=y;
q.push(p);
vis[x][y]=1;//首元素入队
while(!q.empty())
{
p=q.front();
q.pop();
for(int i=0;i<4;i++)
{
x1=p.x+dir[i][0];
y1=p.y+dir[i][1];
if(check(x1,x2))
{
visited[x1][x2]=1;//表示已经经过
temp.x=x1;
temp.y=x2;
q.push(temp);
count[x1][y1]=count[p.x][p.y]+1;//*搜索步数为上一步+1.
}
}
}
}
int main()
{
memset(count1,0,sizeof(count1));
memset(vis,0,sizeof(vis));
/*****/
}
模板二:(模拟队列写法,这里只提供模拟队列的一些常见操作)
int q[]//表示队列
int hh,tt=-1; //hh表示队头,tt表示队尾
//插入操作
q[++tt]=x; //插入元素x
//查询队头
q[hh]
//出队
hh++
//是否为空
if(hh<=tt) 不为空
else 为空
E题:
题干:
https://ac.nowcoder.com/acm/contest/77273/E
小红在出题“小红的走矩阵”时,在数据生成的环节发现了一些问题。
如果直接随机生成一个矩阵,那么结果大概率可以直接输出 n+m−2n + m - 2n+m−2。
如果直接生成极限数据,那么结果也将是跑完整个矩阵,因此可以直接输出 n∗m−1n*m-1n∗m−1。
为了避免以上的情况骗到分,于是小红想到了另一个方案:先随机生成一个从 (1,1)(1,1)(1,1) 到 (n,m)(n,m)(n,m) 的路径,再将路径以外的矩阵的部分全部填成同一个字符。这样一来看似数据确实强,答案并不固定,但实际上也有非常明显的弊端,因为矩阵中大部分都是同一个字符,且这个字符在路径之外,因此选手可以通过“矩阵中是否存在绝对众数”来判断数据是否是这样的数据。
因此小红现在在数据生成的问题上犯了难,她希望小苯帮她来完成本题数据的生成,即请你来代替小苯构造出本题较强的数据。
使得所有的数据都能满足以下的所有条件:1.1.1. 直接输出 n+m−2会返回答案错误,换句话说,“小红走矩阵”这题的正确代码运行后的结果不是 n+m−2
2.2.2. 直接输出 n∗m−1 也会返回答案错误,换句话说,“小红走矩阵”这题的正确代码运行后的结果不是 n∗m−1
3.3.3. 生成的矩阵中,不存在“绝对众数”。(即,没有任何字符的出现次数大于 n∗m/2 向上取整)
4.4.4. 生成的矩阵必须能从 (1,1) 走到 (n,m),换句话说,“小红走矩阵”这题的正确代码运行后的结果不是 −1
思路:
思考这四个限制条件:
第一个条件表示不能拐一个弯,要至少拐两个弯
第二个条件表示不能经过所有点,反正这个条件容易达到,先不管
第三个表示一个字符不能出现一般以上,即要出现一些不同的数
第四个表示 有解
既然是我们来构造那必然是有解,第四个条件不用管,第三个条件只要我们构造是26字符循环就行,关键就是第一个
不妨咱们先给他构造一条带两个弯的路
以5*5的格子的为例:
ababa
xxxxb
xxxxa
xxxcb
xxxab
这样就构造了一条路,现在要让这条路唯一(只能走这条路),必须要让它拐一个弯
ababa
ababb
xxxca
xxxcb
xxxab
如果右下角是
ca
ba
现在来看下x对应的字符有什么影响
因为是最短路径,所以我们在构造时已经把只拐一个弯的封死了,所以x只要不是ab就行
#include <bits/stdc++.h>
using namespace std;
int a[1010][1010];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
if((i&1)==0)
{
a[1][i]=2;
a[2][i]=2;
}
else
{
a[1][i]=1;
a[2][i]=1;
}
}
a[2][m]=3-a[1][m];
for(int i=3;i<=n;i++)
{
a[i][m]=3-a[i-1][m];
}
//单独处理拐弯
a[n][m]=a[n-1][m];
a[n-1][m-1]=3;
a[n][m-1]=3-a[n][m];
a[n-2][m-1]=3;
//构造剩下的
int cnt=2;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!a[i][j])
{
if(cnt==27) cnt=3;
a[i][j]=cnt++;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
cout<<char(a[i][j]+96);
cout<<endl;
}
return 0;
}
F题:
题干:
https://ac.nowcoder.com/acm/contest/77273/F
小红定义一个字符串是“好串”,当且仅当该字符串不包含长度不小于 2 的回文子串。
现在小红拿到了一个仅包含"red"三种字符的字符串,她有如下两个操作:
· 输入 1 x chr:将第xxx个字符修改为chrchrchr
· 输入 2 l r:询问第lll个字符到第rrr个字符的子串再修改最少多少个字符可以变成好串(每次修改后也必须是"red"三种字符中的一种)。
你能帮帮她吗?
思路:
这题咋一看很新颖,毕竟我第一次看到不要回文串的题
回归本题,继续分析:
操作一好说,看操作二
操作二说的是,在[l,r]任意子区间内不允许有回文串
那么,我们想一个问题
回文串是什么样子的呢?换句话说,我怎么去判断是不是回文串
长度为2的回文串
11
长度为三
121
长度为四
1221
长度为五
12321
为啥要枚举长度呢?不知道你发现没有,长度为4包含了长度为2的,长度为5的包含了长度为3的,那么回文串只有两种类型
AA
ABA
又因为题目只能用red(我们将其换成123)
长度为2时 符合条件的好字串为 12 或者 13 23
长度为3 123 132 213 231 312 321
长度为4时 在长度为3的基础上加上一个字符
假设 123 能不能加3?——>不能
能不能加2 --->不能
也就是
ABC后面只能加 A
ABCA 后面加什么呢? 只能加B
也就是说只能是一种长度为3的循环结构(有意思的点)
那么如何去快速判断需要修改那些字符呢?毕竟有六种结构......
那就将6种情况一一列举出来
先预处理出,每一个位置变成每种类型需要的次数,然后边查边修改
因此就需要动态去维护(线段树和树状数组来维护)
坑点来了,线段树要开四倍
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int tree[10][N];
int n,q;
string m[6]={"red","rde","dre","der","edr","erd"};
int lbt(int x)
{
return x&(-x);
}
int sum(int r,int p)
{
int res=0;
for(int i=r;i;i-=lbt(i))
res+=tree[p][i];
return res;
}
void add(int idx,int c,int p)
{
for(int i=idx;i<=N;i+=lbt(i))
tree[p][i]+=c;
}
int ask(int l,int r,int p)
{
return sum(r,p)-sum(l-1,p);
}
int main()
{
cin>>n>>q;
string a;
cin>>a;
string s=' '+a;
for(int i=0;i<6;i++){
for(int j=1;j<=n;j++)
{
if(s[j]!=m[i][(j-1)%3])
add(j,1,i);
}
}
while(q--)
{
int op;
cin>>op;
if(op==1)
{
int idx;
char c;
cin>>idx>>c;
for(int i=0;i<6;i++){
if(s[idx]!=m[i][(idx-1)%3])
add(idx,-1,i);
if(c!=m[i][(idx-1)%3])
add(idx,1,i);
}
s[idx]=c;
}
else
{
int ans=0x3f3f3f3f;
int l,r;
cin>>l>>r;
for(int i=0;i<6;i++)
{
int sum=ask(l,r,i);
ans=min(ans,sum);
}
cout<<ans<<endl;
}
}
return 0;
}