【2022寒假基础集训】第一场 - B.炸鸡块君与FIFA22【倍增DP+集合分类】

Date:2022.04.27
题目描述:
热爱足球(仅限游戏)的炸鸡块君最近购买了FIFA22,并且沉迷于FIFA22的Rivals排位上分。
在该排位系统中,每局游戏可能有胜利(用W表示)、失败(用L表示)、平局(用D表示)三种结果,胜利将使得排位分加一、失败使排位分减一、平局使排位分不变。特别地,该排位系统有着存档点机制,其可以简化的描述为:若你当前的排位分是3的整倍数(包括0倍),则若下一局游戏失败,你的排位分将不变(而不是减一)。
现在,给定一个游戏结果字符串和若干次询问,你需要回答这些询问。
每次询问格式为(l,r,s),询问若你初始有s分,按从左到右的顺序经历了[l,r]这一子串的游戏结果后,最终分数是多少。
输入描述:
输入第一行输入两个整数n,q ( 1 ≤ n , q ≤ 2 × 1 0 5 ) (1≤n,q≤2×10 ^5) (1n,q2×105),表示游戏结果字符串长度与询问次数。
第二行输入一个字符串,表示游戏结果字符串,保证之中只含有W、L、D三种字符。
接下来q行,每行三个数l,r,s ( 1 ≤ l , r ≤ n , 0 ≤ s ≤ 1 0 9 ) (1≤l,r≤n,0≤s≤10 ^9) (1l,rn,0s109) 代表一组询问,询问含义如题面所述。
输出描述:
对于每个询问,输出一个整数,表示该组询问的答案。
示例1
输入
10 7
WLDLWWLLLD
2 6 0
2 6 1
2 6 2
2 6 9
1 7 0
7 10 10
10 10 100

输出
2
2
2
11
1
9
100

思路:首先发现不管从哪里开始,若两个初始状态 % 3 \%3 %3同余,其在经过这段 [ l , r ] [l,r] [l,r]变化后,变化量都一定。因此我们可以根据初始值是多少,由此确定经过这一段后的变化量。
我们将这一段变化量计入状态转移方程:
f [ i ] [ j ] [ k ] : f[i][j][k]: f[i][j][k]:在走这一段区间也就是 [ i , i + 2 j ] [i,i+2^j] [i,i+2j]前,初始值为 k k k。且走完之后,变化量为 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]
由此,我们考虑倍增(这也就是为什么题解是st表,也没有错就是和我们认识的st表不太一样,这说白了就是倍增优化dp)。
所以我们预处理出所有 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]
①当 j = = 0 j==0 j==0
( I ) s t r [ i ] = = ′ W ′ : (I)str[i]=='W': (I)str[i]==W:
f [ i ] [ 0 ] [ k ] = 1 ; f[i][0][k]=1; f[i][0][k]=1;【即经过 [ i , i ] [i,i] [i,i]后增量为 1 1 1
( I I ) s t r [ i ] = = ′ L ′ : (II)str[i]=='L': (II)str[i]==L:
f [ i ] [ 0 ] [ k ] = k − 1 > = 0 ? 1 : 0 ; f[i][0][k]=k-1>=0?1:0; f[i][0][k]=k1>=0?1:0;
【即若经过 [ i , i ] [i,i] [i,i]后为 k − 1 > = 0 k-1>=0 k1>=0,增量为 − 1 -1 1;否则表示 k = = 0 k==0 k==0,因为 0 % 3 = = 0 0\%3==0 0%3==0,所以可不变,增量为 0 0 0。】
( I I I ) s t r [ i ] = = ′ D ′ : (III)str[i]=='D': (III)str[i]==D:
f [ i ] [ 0 ] [ k ] = 0 ; f[i][0][k]=0; f[i][0][k]=0;【即经过 [ i , i ] [i,i] [i,i]后增量为 0 0 0
②当 j ! = 0 j!=0 j!=0:倍增跳。
f [ i ] [ j ] [ k ] = f [ i ] [ j − 1 ] [ k ] + f [ i + ( 1 < < j ) ] [ j − 1 ] [ ( k + f [ i ] [ j − 1 ] [ k ] ) % 3 ] ; f[i][j][k]=f[i][j-1][k]+f[i+(1<<j)][j-1][(k+f[i][j-1][k])\%3]; f[i][j][k]=f[i][j1][k]+f[i+(1<<j)][j1][(k+f[i][j1][k])%3];
注意这里: ( k + f [ i ] [ j − 1 ] [ k ] ) % 3 (k+f[i][j-1][k])\%3 (k+f[i][j1][k])%3是在 i i i前跳 ( 1 < < j ) (1<<j) (1<<j)步,也就是在 i + ( 1 < < j ) − 1 i+(1<<j)-1 i+(1<<j)1处分数 % 3 \%3 %3的结果。
预处理完,查询即每次看区间长度 l e n = = r − l + 1 len==r-l+1 len==rl+1,再倍增跳。
l e n len len的哪一位为 1 1 1(假设为第 j j j位),则从当前位置跳 2 j 2^j 2j步,由此可从 l l l跳至 r r r,并完成整个变化过程。

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10,M=20;
typedef long long LL;
int n,m,q,f[N][M][3],a[N];
char str[N];
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>q;cin>>str+1;
    for(int j=0;j<M;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            for(int k=0;k<3;k++)
                if(!j)
                {
                    if(str[i]=='W') f[i][0][k]=1;
                    else if(str[i]=='L') f[i][0][k]=k-1>=0?-1:0;
                    else if(str[i]=='D') f[i][0][k]=0;
                }
                else f[i][j][k]=f[i][j-1][k]+f[i+(1<<j-1)][j-1][(k+f[i][j-1][k])%3];
    while(q--)
    {
        int l,r,z;cin>>l>>r>>z;
        int len=r-l+1;
        for(int i=M-1;i>=0;i--)
            if(len>>i&1)//哪位为1就跳到哪位
            {
                z=z+f[l][i][z%3];//查表时,当前分数要%3
                l+=(1<<i);//每次区间起点要变化
            }
        cout<<z<<'\n';
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值