2023牛客暑期多校训练营4-题解(L,F,A,J)

L- We are the Lights

题意

一天,波波在参加一个国际灯光展时,注意到一个 n × m n \times m n×m 的网格灯排列成 n n n 行和 m m m 列。起初,所有的灯都熄灭了。

波波注意到,每行每列都有两个按钮,一个用于打开,另一个用于关闭。这意味着他可以执行以下四个操作:

  1. 第二排开启:开启第二排所有灯。
  2. 第二排熄灭:将第二排的所有灯熄灭。
  3. 第二列打开:打开第二列中的所有灯。
  4. 第二列关闭:关闭第二列中的所有灯。

顽皮的波波当然不会错过这个机会。你得到了波波执行的q次操作。在按顺序执行 q q q 次操作后,您需要确定打开的灯的数量。

题解

经典的矩阵覆盖问题,由于后面的操作会覆盖前面已经执行过的操作,因此我们可以从后往前递推结果。

用两个数组分别记录哪些行 r o w row row 和 哪些列 c o l col col 已经被操作覆盖过, r o w s rows rows c o l s cols cols 分别表示当前行和列已经被操作覆盖的数量。

  1. 如果当前行 r o w row row 没有被覆盖且当前的执行操作为 o n on on,则对答案的贡献为 m − c o l s m-cols mcols

  2. 如果当前行 c o l col col 没有被覆盖且当前的执行操作为 o n on on,则对答案的贡献为 n − r o w s n-rows nrows

最后输出累加的答案即可。

时间复杂度为 O ( q ) O(q) O(q)

  • AC_Code
void solve()
{
    int n , m , q ;
    cin >> n >> m >> q ;
    vector< string > s(q) ;
    vector< string > t(q) ;
    vector< int > num(q) ;
    vector< bool > row(n,false) ;
    vector< bool > col(m,false) ;
    for( int i = 0 ; i < q ; ++ i ){
        cin >> s[i] >> num[i] >> t[i] ;
    }
    int res = 0 ;
    int r = 0 , c = 0 ;
    for( int i = q-1 ; i >= 0 ; -- i ){
        int x = num[i] ;
        if( s[i] == "row" ){
            if( !row[x] ){
                row[x] = true ;
                if( t[i] == "on" ){
                    res += m-c ;
                }
                r ++ ;
            }
        }
        else {
            if( !col[x] ){
                col[x] = true ;
                if( t[i] == "on" ){
                    res += n-r ;
                }
                c ++ ;
            }
        }
    }
    cout << res << "\n" ;
    return ;
}

F- Election of the King

题意

在遥远的博博兰,每五年举行一次国王选举。今年是博博兰再次举行国王选举的时候了。博博兰的每个城市都提名了nn国王候选人,编号为 1 ⋯ n 1 \cdots n 1n。这些 n n n 个候选人有明显的政治倾向 a 1 , a 2 , ⋯   , a n ( 1 ≤ a i ≤ 1 0 9 ) a_1,a_2, \cdots,a_n(1 \leq a_i \leq 10^9) a1,a2,,an(1ai109) 代表第二位候选人的政治倾向,其中较大的数字意味着更右翼的倾向, 1 1 1 代表极左翼, 1 0 9 10^9 109 代表极右翼。然后,将在候选人中进行以下内部投票机制,以决定最终的国王:

  1. 将进行 n − 1 n-1 n1 轮投票,每轮只淘汰一名候选人,直到只剩下一名候选人为止,他将成为最终的国王。

  2. 每一轮的投票规则如下:每个候选人都可以投票给除自己之外的任何其他候选人。得票最多的候选人将被淘汰。如果有多个候选人拥有相同的最高票数,其中最右翼的候选人将被淘汰。

在观察了博博兰历届国王选举后,您发现每一位候选人都坚持以不同意见攻击对手的原则,并将在每一轮投票中执行以下策略:

在所有剩余的候选人中,投票给政治倾向与自己最不相同的候选人(即第 i i i 位候选人,如果他们没有被淘汰,将投票给第 j j j 位候选人,他们的票数差最大​的 ∣ a j − a i ∣ |a_j-a_i| ajai , 指尚未被淘汰的人)。如果有多个候选具有相等的 a j − a i a_j-a_i ajai, 他们将投票给他们当中倾向最右翼的人。

题解

通过分析我们可以知道,每次投票的选择只会在最左翼倾向和最右翼倾向的候选人中产生。因此我们可以将候选人的倾向 a i a_i ai 从小到大排序,然后判断被选择出局的人。

a i a_i ai 从小到大排序之后,每次投票后的出局人在剩余候选人中的最左边或者最右边,而决定这两者谁会被投票出局的关键在于最中间的人。因为如果一个候选者投最右边的人,那么其左边的候选者都会投右边( i f   i ≥ x , ∣ a j − a x ∣ ≥ ∣ a j − a i ∣ if \ i \geq x,|a_j-a_x| \geq |a_j-a_i| if ix,ajaxajai),同理如果一个候选者投最左边的人,那么其右边的候选者都会投左边。

因此我们判断谁出局的依据,只需要看投右边的人数有没有到达一半即可。

时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

  • AC_Code
void solve()
{
    int n ;
    cin >> n ;
    vector< int > a(n) ;
    vector< int > b(n) ;
    for( int i = 0 ; i < n ; ++ i ){
        cin >> a[i] ;
        b[i] = a[i] ;
    }
    sort( a.begin() , a.end() ) ;
    int l = 0 ;
    int r = n-1 ;
    while( l < r ){
        int mid = l+r >> 1 ;
        if( a[mid]-a[l] <= a[r]-a[mid] ){
            r -- ;
        }
        else {
            l ++ ;
        }
    }
    for( int i = 0 ; i < n ; ++ i ){
        if( a[l] == b[i] ){
            cout << i+1 << "\n" ;
            return ;
        }
    }
    return ;
}

A- Bobo String Construction

题意

波波正在学习编码,以便给他最好的朋友oboB发送信息。他最近开发了一种“波波编码”,本质上是一种二进制编码。然而,与直接二进制编码不同,“波波编码”由三个部分组成:开头的“开始识别字符串” t t t ,用于传输信息的“消息字符串” s s s,和与“开始识别串”相同的“结束识别字符串”” t t t 。给定需要传输的“消息串” s s s 和识别字符串 t t t,这个“消息字符串” s s s的Bobo编码是 t + s + t t+s+t t+s+t,其中 + + + 表示字符串串联。在发送信息时,波波将他想要发送的消息字符串加密为“波波编码”,并将其逐一发送给oboB。oboB一旦发现识别字符串在接收到的字符串中出现两次,就会停止接收信息,这表明他已经接收到“开始识别字符串”和“结束识别字符串”。请注意,识别字符串可能重叠,例如,如果识别字符串为 t = 101 t=101 t=101,oboB接收 10101 10101 10101,oboB会认为识别字符串已经出现两次,并停止接收信息。

现给定一个 01 01 01 字符串 t t t,和一个正整数 n n n,求构造一个 01 01 01 字符串 s s s,使得 t + s + t t+s+t t+s+t 可以成立。

题解

构造 s s s 为全 0 0 0 字符串或者全 1 1 1 字符串即可,然后判断两者谁满足题意即可。

时间复杂度 O ( T n ) O(Tn) O(Tn)

  • AC_Code
void solve()
{
    int n ;
    cin >> n ;
    string t ;
    cin >> t ;
    int tlen = t.size() ;
    string s = "" ;
    for( int i = 1 ; i <= n ; ++ i ){
        s += "1" ;
    }
    string ans = t+s+t ;
    if( ans.find( t , 1 ) == n+tlen ){
        cout << s << "\n" ;
    }
    else {
        for( int i = 1 ; i <= n ; ++ i ){
            cout << 0 ;
        }
        cout << "\n" ;
    }
    return ;
}

J- Qu’est-ce Que C’est?

题意

作为一名记者,波波被要求写一篇关于波波兰的长篇报道。波波最初想写一份既赞扬又批评的报告,但他担心恶意的人可能会断章取义,把他的话变成对波波兰的批评。长度为 n n n 的报告可以看作是一个整数序列 a 1 , … , a n a_1, \dots,a_n a1,,an − m ≤ a i ≤ m -m \leq a_i \leq m maim ,表示被称赞程度为 a i a_i ai。如果报告中有 k k k 个连续的声明总赞度小于 0 0 0,并且满足 k ≥ 2 k \geq 2 k2,那么这 k k k 段声明可能会被恶意人员断章取义,攻击波波。波波不希望这种情况发生,他想知道在不断章取义的情况下,他能写多少不同的报告。

形式上,您需要计算长度为 n n n 的整数序列 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an​,满足:

  • 对于所有的 1 ≤ i ≤ n 1 \leq i \leq n 1in,保证 − m ≤ a i ≤ m -m \leq a_i \leq m maim

  • 对于所有的 1 ≤ l ≤ r ≤ n 1 \leq l \leq r \leq n 1lrn,保证 ∑ i = l r a i ≥ 0 \sum_{i=l}^{r}a_i \geq 0 i=lrai0

由于答案可能很大,您需要对 998244353 998244353 998244353 取模。

题解

动态规划求解,定义 d p i , j dp_{i,j} dpi,j 表示前 i i i 个数,当前的数为 j j j 的满足题意的数量,定义 p r e i , j pre_{i,j} prei,j 表示第 i i i 个数, ∑ x = 0 j d p i , x \sum_{x=0}^{j}dp_{i,x} x=0jdpi,x

那么状态转移方程就是:

d p i , j { p r e i , 2 m − p r e i , 2 m − j − 1 i f   j < m p r e i , 2 m − p r e i , j − m − 1 i f   j ≥ m dp_{i,j} \begin{cases} pre_{i,2m}-pre_{i,2m-j-1} &if \ j \lt m \\ pre_{i,2m}-pre{i,j-m-1} &if \ j \geq m \end{cases} dpi,j{prei,2mprei,2mj1prei,2mprei,jm1if j<mif jm

同时更新 p r e i , j pre_{i,j} prei,j,最后答案即为 a n s = ∑ x = 0 2 m d p n , x ans = \sum_{x=0}^{2m}dp_{n,x} ans=x=02mdpn,x,注意在运算过程中取模。

时间复杂度为 O ( n m ) O(nm) O(nm)

  • AC_Code
int pre[5005][10005] ;
int dp[5005][10005] ;
void solve()
{
    int n , m ;
    cin >> n >> m ;
    for( int i = 0 ; i <= (m<<1) ; ++ i ){
        dp[1][i] = 1 ;
        pre[1][i] = pre[1][i-1] + dp[1][i]  ;
    }
    for( int i = 2 ; i <= n ; ++ i ){
        for( int j = 0 ; j <= (m<<1) ; ++ j ){
            if( j < m ){
                dp[i][j] = ( ( pre[i-1][(m<<1)]-pre[i-1][ (m<<1)-j-1 ] )%Max +Max ) %Max ;
            }
            else {
                dp[i][j] = ( ( pre[i-1][(m<<1)]-pre[i-1][j-m-1] )%Max +Max) %Max ;
            }
            pre[i][j] = ( pre[i][j-1]+dp[i][j] )%Max ;
        }
    }
    int res = 0 ;
    for( int i = 0 ; i <= (m<<1) ; ++ i ){
        res = ( res + dp[n][i] ) %Max ;
    }
    cout << res << "\n" ;
    return ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值