题目链接
思路
d
p
[
i
]
:
dp[i]:
dp[i]:表示以
i
i
i为结尾的所有方案数。
假设前一位数是
j
j
j,再前一位数是
k
k
k,连续的三个数满足条件
k
<
j
<
i
k<j<i
k<j<i,那么当
k
=
i
⨁
j
k=i \bigoplus j
k=i⨁j时便是不合法的方案,需要减去。所以可以列出这样的
d
p
dp
dp方程:
d
p
[
i
]
=
∑
j
=
1
i
−
1
(
d
p
[
j
]
−
d
p
[
j
⨁
i
]
)
dp[i]=\sum_{j=1}^{i-1}(dp[j]-dp[j \bigoplus i])
dp[i]=∑j=1i−1(dp[j]−dp[j⨁i])。
对上述式子的括号拆除即是
d
p
[
i
]
=
∑
j
=
1
i
−
1
d
p
[
j
]
−
∑
k
<
k
⨁
i
<
i
d
p
[
k
]
dp[i]=\sum_{j=1}^{i-1}dp[j]-\sum_{k<k\bigoplus i<i}dp[k]
dp[i]=∑j=1i−1dp[j]−∑k<k⨁i<idp[k]。
当计算
d
p
[
i
]
dp[i]
dp[i]时,
∑
j
=
1
i
−
1
d
p
[
j
]
\sum_{j=1}^{i-1}dp[j]
∑j=1i−1dp[j]中的每个
d
p
[
j
]
dp[j]
dp[j]其实都已经算完,那么可以用一个
s
u
m
[
i
−
1
]
sum[i-1]
sum[i−1]数组记录
∑
j
=
1
i
−
1
d
p
[
j
]
\sum_{j=1}^{i-1}dp[j]
∑j=1i−1dp[j]。
接下来需要考虑的是如果已经确定了
i
i
i,如何处理
j
,
k
j,k
j,k的关系。
j
j
j的所有情况我们已经用
s
u
m
[
i
−
1
]
sum[i-1]
sum[i−1]计算掉了,所以需要考虑这个式子的后半部分和这个
k
k
k到底如何去取。
假定对于一个
i
i
i的二进制表示为
1101010
1101010
1101010。因为考虑的是
i
⨁
j
⨁
k
=
0
i \bigoplus j \bigoplus k=0
i⨁j⨁k=0且
1
≤
k
<
j
<
i
1\leq k<j<i
1≤k<j<i的情况,所以对于
i
,
j
,
k
i,j,k
i,j,k相同的位,最多同时出现两个
1
1
1,最高位已经在
i
i
i中出现了一个1,要保证
j
>
k
j>k
j>k,所以要保证
j
j
j的最高位也为1,而
k
k
k的最高位为0。当确定了
j
j
j的最高位,
j
j
j后面的位数上的01就可以在满足
j
<
i
j<i
j<i的情况下随意表示了。然后考虑
k
k
k,因为
k
≥
1
k \geq 1
k≥1,所以
k
k
k至少有一位为
1
1
1。接下来就考虑
k
k
k后面几位的01表示,如果
k
k
k确定,那么也可唯一确定
j
j
j。暴力找
k
k
k的复杂度到达了O(n),可以根据二进制的性质,我们枚举
i
i
i在二进制表示下除最高位的每一位,若这一位为
1
1
1,那么就令这一位是
k
k
k的最高位。假设这一位是第
d
d
d位,
k
k
k为最高位所有可表示的数字范围即是
(
1
<
<
d
)
≤
k
<
(
1
<
<
(
d
+
1
)
−
1
)
)
(1<<d) \leq k <(1<<(d+1)-1))
(1<<d)≤k<(1<<(d+1)−1)),因为
k
k
k的前几位都是0,所以对应的
j
j
j的那几位也都是0,且第
d
d
d位上已经有两个1了,所以
j
j
j除最高位以外的1的位置一定小于第
d
d
d位,那么此时无论后面几位的情况如何,都能满足
j
<
i
j<i
j<i。
例如这个例子:(x表示可以随意取)
i
:
101000
i:101000
i:101000
j
:
1
x
x
x
x
x
j:1xxxxx
j:1xxxxx
k
:
0
x
x
x
x
x
k:0xxxxx
k:0xxxxx
在这种情况下,当
k
k
k的第三位当成最高位且取1时,
k
:
001
x
x
x
k:001xxx
k:001xxx,
j
:
100
x
x
x
j:100xxx
j:100xxx,那么就能确定
j
j
j的前几位,那么
j
j
j后面几位不管怎么取一定满足
j
<
i
j<i
j<i。
若
k
k
k的最高位不是在第三位取
1
1
1,假设是在第二维上。即
k
:
0001
x
x
k:0001xx
k:0001xx,那么对于第三位上要满足条件,
j
j
j就变成了
j
:
1011
x
x
j:1011xx
j:1011xx,这种情况下可以显而易见的发现
j
>
i
j>i
j>i不满足条件了。
所以对于考虑后面枚举所有
k
k
k的情况就变成了当确定了
k
k
k的最高位
d
d
d,那么区间上所有的表示就变成了
s
u
m
[
(
1
<
<
(
d
+
1
)
−
1
)
]
−
s
u
m
[
(
1
<
<
d
)
−
1
]
sum[(1<<(d+1)-1)]-sum[(1<<d)-1]
sum[(1<<(d+1)−1)]−sum[(1<<d)−1]。具体可以看代码。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
const int mod = 998244353;
LL dp[N], sum[N];
void solve() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
dp[i] = sum[i - 1] + 1; // +1表示只有一个数字,且这个数字就是i的情况。
int x = 1;
while(x << 1 < i) {
if(x & i) {
dp[i] = (dp[i] - (sum[(x << 1) - 1] - sum[x - 1]) + mod) % mod;
}
x <<= 1;
}
sum[i] = (sum[i - 1] + dp[i]) % mod;
}
printf("%lld\n", sum[n]);
}
int main() {
// freopen("in.txt", "r", stdin);
solve();
return 0;
}