2022牛客寒假1 炸鸡块君与FIFA22(做法一:倍增 st表)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题意:

如题。

思路:

这是一道关于 倍增倍增算法讲解) 的一道典型例题,同时还涉及到了 数论中 对于 的概念,是一道好题。

我们注意到 起始分数若在 % 3 意义下相等(即这些起始分值 3 结果一致),则 经历 [l, r] 一段后分数的变化量是一个定值重要

关于查询ask

  • 假设我们 已经预处理出了 st

  • 则对于 每一次询问 ask,可以 先将某初始分数 start3 取模,然后按照 倍增的套路l若干个 2 的次幂 跳到 r

  • 解释:由于 任意正整数 都能由 若干个 2 的次幂 表达,显然 一个长度为正整数的区间 也可以由 若干长度为 2 的若干次幂区间 组合而成,代码中有体现)

  • 注意:跳的时候 要按照 分数对 3 取模的结果 来决定访问 哪个 st。(具体见代码)

关于 st 表的预处理 init(关于 预处理 可以参考:倍增算法讲解 + 例题,两者类似)
st[i][j][k] 状态表示:
  • 定义 st[3][200010][21] 之中, st[k][i][j] 表示 在初始分数模 3 的结果为 k 的情况下 经历了 [i, i + 2 ^ j - 1] 一段游戏后 分数的 变化量;
st[i][j][k] 状态计算:
  • 首先,按照字符串内容初始化 j = 0 的情况;

  • 之后,st[k][i][0] st[k][i][j - 1]st[k][mid][j - 1] 计算得到,之中 mid = i + 2 ^ (j - 1)

状态转移方程:
  • st[k][i][j] = st[k][i][j - 1] + st[(k + st[k][i][j - 1]) % 3][mid][j - 1]k = 0、1、2

  • 其中 (k + st[k][i][j - 1]) % 3 表示:如果在 i 处初始分数 模 3 的结果 为 k,那么到了 mid 处时分数对 3 取模得多少

时间复杂度:

O ( n l o g n + q ) O(nlogn + q) O(nlogn+q)

代码:

#include<bits/stdc++.h>

using namespace std;
const int N = 2e5+10, M = 21;
int n;
int q;
char s[N];
int st[3][N][M];

void init()
{
    for(int j=0; j<M; ++j)
    {
        for(int i=1; i+(1<<j)-1<=n; ++i)
        {
            if(!j)//根据字符串的内容,我们要对 st 数组进行初始化,根据定义以及题意出发即可
            {
                if(s[i]=='W')//赢 情况
                {
                    st[0][i][j] = st[1][i][j] = st[2][i][j] = 1;
                }
                else if(s[i]=='L')//输 情况 按照题意要根据分数模 3 的结果进行赋值
                {
                    st[0][i][j] = 0;
                    st[1][i][j] = -1;
                    st[2][i][j] = -1;
                }
                else//平局 情况 
                {
                    st[0][i][j] = st[1][i][j] = st[2][i][j] = 0;
                }
            }
            else
            {
                int mid = i+(1<<(j-1));//设置 mid 为区间 [i, j] 的中点,这是倍增算法的套路
                for(int k=0; k<3; ++k)//依据推出来的状态转移方程
                {
                    st[k][i][j] = st[k][i][j-1] + st[((k+st[k][i][j-1])%3+3)%3][mid][j-1];
                }
            }
        }
    }
}

int ask(int l, int r, int start)
{
    int len = r - l + 1;
    //将长度为 len 的区间划分为若干 长度为 2 的若干次幂 的区间
    while(len)
    {
        int k = log(len) / log(2);
        start += st[start%3][l][k];//跳的时候 根据题意一定要根据 模数 来跳
        len -= (1<<k), l += (1<<k);
    }
    return start;
}

int main()
{
    cin>>n>>q;
    cin>>s+1;
    
    init();
    
    while(q--)
    {
        int l, r, start;
        scanf("%d%d%d", &l, &r, &start);
        printf("%d\n", ask(l, r, start));
    }
    
    return 0;
}

本题做法二:2022牛客寒假1 炸鸡块君与FIFA22(做法二:线段树)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值