题目
题意:给定一个数组
a
a
a和
p
p
p。数组中每个元素互不相同。给定无限合S,
x
∈
S
x\in S
x∈S如果
y
∈
a
y\in a
y∈a或
y
=
2
∗
x
+
1
,
x
∈
S
y=2*x+1,x\in S
y=2∗x+1,x∈S或
y
=
4
∗
x
,
x
∈
S
y=4*x,x\in S
y=4∗x,x∈S
问S有多少元素,小于
2
p
2^p
2p。
1
<
=
n
,
p
<
=
1
0
5
1<=n,p<=10^5
1<=n,p<=105
参考官方
思路:定义
f
(
x
)
f(x)
f(x)表示
x
x
x的最高二进制位,那么有
f
(
2
∗
x
)
=
f
(
x
)
+
1
,
f
(
4
∗
x
)
=
f
(
x
)
+
2
f(2*x)=f(x)+1,f(4*x)=f(x)+2
f(2∗x)=f(x)+1,f(4∗x)=f(x)+2。
定义dp[x]表示最高二进制位为
x
x
x的元素有多少个。那么有
d
p
[
x
]
=
d
p
[
x
−
1
]
+
d
p
[
x
−
2
]
+
g
(
x
)
dp[x]=dp[x-1]+dp[x-2]+g(x)
dp[x]=dp[x−1]+dp[x−2]+g(x),其中
g
(
x
)
g(x)
g(x)表示最高二进制位为
x
x
x的
a
a
a元素个数。
注意,由于某些
a
i
a_i
ai可能由其他的
a
j
a_j
aj衍生而成,我们需要去重。
将
a
a
a数组排序,然后检查当前元素
a
i
a_i
ai是否可能由
a
1
,
.
.
.
,
a
i
−
1
a_1,...,a_{i-1}
a1,...,ai−1的通过
2
∗
x
+
1
,
4
∗
x
2*x+1,4*x
2∗x+1,4∗x生成。递归校验即可。
详见代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 200010;
const int mod = 1000000007;
int n, p, a[maxn];
set<int> st;
int dp[maxn];
bool check(int x) {
if (st.find(x) != st.end()) {
return false;
}
if ((x - 1) % 2 == 0 && (x - 1) / 2 > 0 && !check((x - 1) / 2)) {
return false;
}
if (x % 4 == 0 && x / 4 > 0 && !check(x / 4)) {
return false;
}
return true;
}
int cal(int x) {
int count = -1;
while (x) {
++count;
x >>= 1;
}
return count;
}
int Add(int x, int y) {
return (x * 1LL + y) % mod;
}
void solve() {
scanf("%d%d", &n, &p);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
sort(a + 1, a + n + 1);
st.clear();
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; ++i) {
if (check(a[i])) {
st.insert(a[i]);
++dp[cal(a[i])];
}
}
if (p == 1) {
printf("%d\n", dp[0]);
return;
}
dp[1] = Add(dp[1], dp[0]);
int res = Add(dp[0], dp[1]);
for (int i = 2; i < p; ++i) {
dp[i] = Add(Add(dp[i], dp[i-1]), dp[i-2]);
res = Add(res, dp[i]);
}
printf("%d\n", res);
}
int main() {
int t;
// scanf("%d", &t);
t = 1;
while (t--) {
solve();
}
}
/*
1 3 4 7 9 12 15 16 19 25 28
*/