codeforces 1097H. Mateusz and an Infinite Sequence
分析
题目大意:
给你一个长度为
d
d
d的数组
g
e
n
gen
gen和模数
m
m
m,
定义序列
M
k
M_k
Mk如下:
M
0
=
0
M_0={0}
M0=0
M
k
(
k
>
=
1
)
M_k(k>=1)
Mk(k>=1)是将
M
k
−
1
M_{k-1}
Mk−1复制
d
d
d份,且复制的第i份的所有元素都要加上
g
e
n
i
gen_i
geni。
保证
g
e
n
1
=
0
gen_1=0
gen1=0,给你一个序列
B
B
B和两个数
l
,
r
l,r
l,r,询问
M
∞
M_{\infty}
M∞的
[
l
,
r
]
[l,r]
[l,r]区间中有多少个长度为
∣
B
∣
|B|
∣B∣的子串
A
A
A满足
A
≤
B
A \le B
A≤B在
间共出现了多少次。
2
<
=
d
<
=
20
,
2
<
=
m
<
=
60
,
∣
B
∣
<
=
30000
,
1
<
=
l
<
=
r
<
=
1
0
18
2<=d<=20,2<=m<=60,|B|<= 30000,1<=l<=r<=10^{18}
2<=d<=20,2<=m<=60,∣B∣<=30000,1<=l<=r<=1018
首先由于
g
e
n
1
=
0
gen_1=0
gen1=0,所以原来的那个串是一定会保留的。
我们考虑
∣
B
∣
=
1
|B|=1
∣B∣=1的情况,这个时候只需要记录每个数的出现次数,
g
e
n
i
gen_i
geni相当于是把当前的答案数组循环位移一段加回去,考虑
f
[
i
]
[
v
]
f[i][v]
f[i][v]表示
M
0
=
v
M_0=v
M0=v时复读了
i
i
i轮的答案,
f
[
x
]
[
i
]
=
∑
j
=
0
m
−
1
f
[
x
−
1
]
[
k
+
g
e
n
[
j
]
m
o
d
  
m
]
f[x][i]=\sum_{j=0}^{m-1} f[x-1][k+gen[j] \mod m]
f[x][i]=∑j=0m−1f[x−1][k+gen[j]modm],最后用类似数位Dp的方法可以求出答案。
这个时候考虑
∣
B
∣
≠
1
|B|\neq 1
∣B∤=1的情况。我们发现,数位Dp那部分的方法其实可以沿用,仍然记录
f
[
i
]
[
v
]
f[i][v]
f[i][v]表示
M
0
=
v
M_0=v
M0=v时复读了
i
i
i轮的信息。注意到我们并不能单纯地记录答案。因为
f
[
x
]
[
i
]
f[x][i]
f[x][i] 实际上是要合并所有
j
j
j的
f
[
x
−
1
]
[
k
+
g
e
n
[
j
]
m
o
d
  
m
]
f[x-1][k+gen[j] \mod m]
f[x−1][k+gen[j]modm]的信息。事实上每个
f
[
x
−
1
]
[
k
+
g
e
n
[
j
]
m
o
d
  
m
]
f[x-1][k+gen[j] \mod m]
f[x−1][k+gen[j]modm]都代表了一个长度为
d
x
−
1
d^{x-1}
dx−1的区间的信息。我们现在需要考虑的就转化成了两个区间的答案怎么合并。
首先肯定拿
B
B
B去各自匹配两个子区间,然后一种显然的思路是暴力记录一下两个子区间的前缀和后缀匹配
B
B
B的情况,合并的时候暴力枚举。
这样的话复杂度是
O
(
m
n
d
l
o
g
d
r
)
O(mndlog_dr)
O(mndlogdr),算一下发现超了一点点,需要优化。
考虑一下具体的做法是:
我们设
s
u
f
i
suf_i
sufi表示当前子区间的
N
−
i
N-i
N−i后缀可以和
B
B
B的
N
−
i
N-i
N−i前缀匹配,
p
r
e
i
pre_i
prei表示当前子区间的
i
i
i前缀可以和
B
B
B的
i
i
i后缀匹配。那么我们发现答案就是
∑
[
s
u
f
i
&
p
r
e
i
]
\sum [suf_i \& pre_i]
∑[sufi&prei]。所以可以用
b
i
t
s
e
t
bitset
bitset优化之。复杂度
O
(
m
n
w
d
l
o
g
d
r
)
O(m\frac{n}{w}dlog_dr)
O(mwndlogdr)
注意细节。
代码
#include<bits/stdc++.h>
const int N = 3e4 + 10;
typedef std::bitset<N> BI;
long long ri() {
char c = getchar(); long long x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
long long len[66];
int n, d, m, tot, gen[25], b[N];
BI all;
struct info {
long long ans, len;
BI pre, suf;
info operator + (const info &b) { //Merge this and b
info c;
c.ans = ans + b.ans;
c.len = len + b.len;
c.pre = pre; c.suf = b.suf;
if(len < n - 1) c.pre &= (b.pre >> len) | (all << n - 1 - len);
if(b.len < n - 1) c.suf &= (suf << b.len) | (all >> n - 1 - b.len);
if(len + b.len >= n) {
BI res = suf & b.pre;
if(len < n - 1) res &= (all >> n - 1 - len);
if(b.len < n - 1) res &= (all << n - 1 - b.len);
c.ans += res.count();
}
return c;
}
void operator += (const info &b) {*this = !len ? b : *this + b;}
}f[66][66];
long long Query(long long x) {
info res; res.ans = res.len = 0;
int add = 0;
for(int i = tot; ~i; --i)
for(int j = 1;j <= d; ++j) {
if(x >= len[i]) {
x -= len[i];
res += f[i][(add + gen[j]) % m];
}
else {
(add += gen[j]) %= m;
break;
}
}
return res.ans;
}
int main() {
d = ri(); m = ri();
for(int i = 1;i <= d; ++i)
gen[i] = ri();
n = ri();
for(int i = 1;i <= n; ++i)
b[i] = ri();
for(int i = 1;i < n; ++i)
all.set(i);
len[tot = 0] = 1;
for(int i = 0;i < m; ++i) { //init
f[0][i].len = 1;
if(n == 1) f[0][i].ans = i <= b[1];
else {
for(int j = 1;j <= n - 1; ++j) {
f[0][i].pre[j] = i <= b[j + 1];
f[0][i].suf[j] = i <= b[j];
}
}
}
long long L = ri(), R = ri();
for(;len[tot] <= R / d;) { //transformation
++tot; len[tot] = len[tot - 1] * d;
for(int i = 0;i < m; ++i)
for(int j = 1;j <= d; ++j)
f[tot][i] += f[tot - 1][(i + gen[j]) % m];
}
printf("%lld\n", Query(R) - Query(L + n - 2));
return 0;
}