[数位DP + 三进制状态压缩] K - Balanced Numbers SPOJ - BALNUM

本文介绍了一种使用状态压缩动态规划方法来计算指定区间内平衡数的数量。平衡数是一种特殊的整数,其特点在于所有偶数位上的数字出现次数为奇数次,而所有奇数位上的数字出现次数为偶数次。文章详细解释了如何通过三进制状态压缩来高效地解决这一问题。

BALNUM - Balanced Numbers

no tags 

 

Balanced numbers have been used by mathematicians for centuries. A positive integer is considered a balanced number if:

1)      Every even digit appears an odd number of times in its decimal representation

2)      Every odd digit appears an even number of times in its decimal representation

For example, 77, 211, 6222 and 112334445555677 are balanced numbers while 351, 21, and 662 are not.

Given an interval [A, B], your task is to find the amount of balanced numbers in [A, B] where both A and B are included.

Input

The first line contains an integer T representing the number of test cases.

A test case consists of two numbers A and B separated by a single space representing the interval. You may assume that 1 <= A <= B <= 1019 

Output

For each test case, you need to write a number in a single line: the amount of balanced numbers in the corresponding interval

Example

Input:
2
1 1000
1 9
Output:
147
4

 Submit solution!

 

/// 每个数出现次数的可能性 0 奇数 偶数
/// 分别用0 1 2 表示 
/// 三进制的状态压缩
#include <bits/stdc++.h>
using namespace std;

int ch[30];
long long dp[30][60000];
int mi[11] = {1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049};

bool judge(int sum)
{
	int a[10];  // 各数字出现次数状态
	for (int i = 9; i >= 0; i--)
	{
		a[i] = sum / mi[i];
		sum %= mi[i];
	}

	bool ff = 1;
	for (int i = 9; i >= 0; i--)
	{
		if (a[i] == 0)
			continue;
		if (i % 2 == 0 && a[i] == 2)
			ff = 0;
		if (i % 2 == 1 && a[i] == 1)
			ff = 0;
	}

	if (!ff)
		return 0;
	else
		return 1;
}

int change (int sum, int i)
{
	int t = (sum % mi[i + 1]) / mi[i]; // i 出现次数的状态
	if (t == 2)
		sum -= mi[i];
	else
		sum += mi[i];
	return sum;
}

long long dfs(int pos, int sum, bool lim)
{
	if (pos == 0)
		return judge(sum);

	if (!lim && dp[pos][sum] != -1)
		return dp[pos][sum];

	long long res = 0;
	int up = lim ? ch[pos] : 9;
	for (int i = 0; i <= up; i++)
	{
		/// 前导零
		res += dfs(pos - 1, sum == 0 && i == 0 ? 0 : change(sum, i), lim && (i == up));
	}
	if (!lim)
		dp[pos][sum] = res;
	return res;
}

long long solve(long long x)
{
	int w = 0;
	while (x)
	{
		ch[++w] = x % 10;
		x /= 10;
	}
	return dfs(w, 0, 1);
}

int main()
{
	memset(dp, -1, sizeof dp);
	int t;
	scanf("%d", &t);
	while (t--)
	{
		long long a, b;
		scanf("%lld %lld", &a, &b);
		printf("%lld\n", solve(b) - solve(a - 1));
	}
	return 0;
}

 

### Height-Balanced Binary Tree 与 Self-Balanced Binary Tree 的区别 **Height-Balanced Binary Tree** 是一种二叉树,其定义为:对于树中的每个节点,其左右子树的深度之差不超过1。这种平衡性确保了树的整体高度保持在 $ O(\log n) $ 级别,从而保证了查找、插入和删除操作的时间复杂度接近最优。例如,在 LeetCode 题目中,判断一棵二叉树是否是高度平衡的通常涉及递归计算每个节点的左右子树深度,并检查它们的差异[^1]。 **Self-Balanced Binary Tree** 则是一个更广泛的概念。它不仅要求树的高度平衡,还要求在进行插入或删除操作后,树能够通过特定的旋转操作(如左旋、右旋)自动恢复平衡。常见的自平衡二叉搜索树包括 AVL 树 和 红黑树(Red-Black Tree)。AVL 树是一种特殊的高度平衡二叉搜索树,其每个节点的左子树和右子树的高度差最多为1,并且所有操作(插入、删除)都会维持这一性质;而红黑树则通过颜色规则来保证树的大致平衡,虽然它的高度可能略高于 AVL 树,但其插入和删除操作的性能更好[^3]。 #### 关键区别 1. **定义上的区别**: - Height-Balanced Binary Tree 只要求任意节点的左右子树深度差不超过1。 - Self-Balanced Binary Tree 不仅要求高度平衡,还需要支持动态操作(插入、删除)后的自动平衡维护。 2. **应用场景**: - Height-Balanced Binary Tree 通常用于静态结构或不需要频繁更新的场景。 - Self-Balanced Binary Tree 更适合需要频繁插入和删除的动态数据结构,例如数据库索引和语言标准库中的有序集合。 3. **实现机制**: - Height-Balanced Binary Tree 的实现较为简单,只需检查每个节点的子树深度差即可。 - Self-Balanced Binary Tree(如 AVL 树)则需要额外的旋转操作来维持平衡,例如 AVL 树的单旋转和双旋转[^3]。 4. **性能特性**: - 在查找操作较多的情况下,Height-Balanced Binary Tree 和 Self-Balanced Binary Tree 的性能相近。 - 在插入和删除操作较多的情况下,Self-Balanced Binary Tree(如红黑树)通常表现更好,因为它们的平衡策略减少了旋转的次数。 ### 示例代码:AVL 树的插入操作 以下是一个 AVL 树的插入操作示例,展示了如何通过旋转保持树的平衡: ```cpp struct Node { int key; Node *left; Node *right; int height; }; int height(Node *N) { if (N == NULL) return 0; return N->height; } Node* newNode(int key) { Node* node = new Node(); node->key = key; node->left = NULL; node->right = NULL; node->height = 1; return node; } Node* rightRotate(Node *y) { Node *x = y->left; Node *T2 = x->right; x->right = y; y->left = T2; y->height = max(height(y->left), height(y->right)) + 1; x->height = max(height(x->left), height(x->right)) + 1; return x; } Node* leftRotate(Node *x) { Node *y = x->right; Node *T2 = y->left; y->left = x; x->right = T2; x->height = max(height(x->left), height(x->right)) + 1; y->height = max(height(y->left), height(y->right)) + 1; return y; } int getBalance(Node *N) { if (N == NULL) return 0; return height(N->left) - height(N->right); } Node* insert(Node* node, int key) { if (node == NULL) return newNode(key); if (key < node->key) node->left = insert(node->left, key); else if (key > node->key) node->right = insert(node->right, key); else return node; node->height = 1 + max(height(node->left), height(node->right)); int balance = getBalance(node); if (balance > 1 && key < node->left->key) return rightRotate(node); if (balance < -1 && key > node->right->key) return leftRotate(node); if (balance > 1 && key > node->left->key) { node->left = leftRotate(node->left); return rightRotate(node); } if (balance < -1 && key < node->right->key) { node->right = rightRotate(node->right); return leftRotate(node); } return node; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值