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)
(1≤n,q≤2×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)
(1≤l,r≤n,0≤s≤109) 代表一组询问,询问含义如题面所述。
输出描述:
对于每个询问,输出一个整数,表示该组询问的答案。
示例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]=k−1>=0?1:0;
【即若经过
[
i
,
i
]
[i,i]
[i,i]后为
k
−
1
>
=
0
k-1>=0
k−1>=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][j−1][k]+f[i+(1<<j)][j−1][(k+f[i][j−1][k])%3];
注意这里:
(
k
+
f
[
i
]
[
j
−
1
]
[
k
]
)
%
3
(k+f[i][j-1][k])\%3
(k+f[i][j−1][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==r−l+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;
}