0
点赞
收藏
分享

微信扫一扫

Infinite Set(dp/数论/dfs)

汤姆torn 2022-02-22 阅读 20

题目
题意:给定一个数组 a a a p p p。数组中每个元素互不相同。给定无限合S, x ∈ S x\in S xS如果
y ∈ a y\in a ya y = 2 ∗ x + 1 , x ∈ S y=2*x+1,x\in S y=2x+1,xS y = 4 ∗ x , x ∈ S y=4*x,x\in S y=4x,xS
问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(2x)=f(x)+1,f(4x)=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[x1]+dp[x2]+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,...,ai1的通过 2 ∗ x + 1 , 4 ∗ x 2*x+1,4*x 2x+1,4x生成。递归校验即可。
详见代码

#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
*/
举报

相关推荐

0 条评论