牛客周赛-round 36 补题

前言:

这次周赛的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;
}

  • 55
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值