SPOJ BALNUM – Balanced Numbers(数位DP+状态压缩)

传送门:SPOJ BALNUM

【题意】求出区间[A,B]内的Balanced Numbers数的个数,所谓Balanced Numbers数就是满足:按十进制位每个出现过的数(0~9),如果是偶数出现奇数次,奇数出现偶数次的数(前导零不能算)。

【分析】数位DP,dp[len][odd][even]表示长度为len的数,出现奇数次的数字集合s和出现偶数次的集合even的数字个数。转移很简单,如果数字没出现过(即odd和even中都没有)则把它添加到odd中,然后odd中出现过则去掉加到even中,even也类似,需要注意前导零即可。网上好多用三进制做的,我图简单就2进制了,空间复杂度高了很多,搜索时间也慢了些。

【AC CODE】20ms

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <set>
//#include <unordered_set>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef unsigned long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 21;
LL dp[MAXN][1<<10][1<<10];
bool vis[MAXN][1<<10][1<<10];
int bit[MAXN];

bool ok(int s, int odd)
{
	rep(i,0,10)
	{
		if((1<<i)&s)
		{
			if((odd&(1<<i)) && (i&1)) return false;
			if(!(odd&(1<<i)) && !(i&1)) return false;
		}
	}
	return true;
}
LL dfs(int len, int s, int odd, bool ismax, bool z)
{
	if(!len) return ok(s,odd);
	LL &ans = dp[len][s][odd];
	if(!ismax && vis[len][s][odd]) return ans;
	LL sum = 0;
	int mx = ismax?bit[len]:9;
	repe(i,0,mx)
	{
		int nxs = s, nxodd = odd;
		if(!z || i) nxs = s|(1<<i), nxodd = odd^(1<<i);
		sum += dfs(len-1,nxs,nxodd,ismax&&i==mx,z&&0==i);
	}
	if(!ismax)
	{
		ans = sum;
		vis[len][s][odd] = true;
	}
	return sum;
}
LL sloved(LL n)
{
	int len = 0;
	while(n)
	{
		bit[++len] = n%10;
		n /= 10;
	}
	return dfs(len,0,0,1,1);
}
int main()
{
#ifdef SHY
	freopen("d:\\1.txt", "r", stdin);
#endif
	int t;
	scanf("%d", &t);
	clc(vis,0);
	while(t--)
	{
		LL x,y;
		scanf("%llu %llu", &x, &y);
		printf("%llu\n", sloved(y)-sloved(x-1));
	}
	return 0;
}

三进制解法,空间小了很多,时间差不多。

【AC CODE】20ms

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <set>
//#include <unordered_set>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef unsigned long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 21;
LL dp[MAXN][59049+10];
bool vis[MAXN][59049+10];
int bit[MAXN],buf[MAXN];

int to3(int s)
{
	clc(buf,0);
	int len = 0;
	while(s)
	{
		buf[len++] = s%3;
		s /= 3;
	}
	return len;
}
int to10(int *c, int len)
{
	int ans = 0;
	per(i,len-1,0)
		ans = ans*3+c[i];
	return ans;
}
bool ok(int s)
{
	int n = to3(s);
	rep(i,0,n)
	{
		if(!buf[i]) continue;
		if((i&1) && buf[i] == 1) return false;
		if(!(i&1) && buf[i] == 2) return false;
	}
	return true;
}
LL dfs(int len, int s, bool ismax, bool z)
{
	if(!len) return ok(s);
	LL &ans = dp[len][s];
	if(!ismax && vis[len][s]) return ans;
	int mx = ismax?bit[len]:9;
	LL sum = 0;
	repe(i,0,mx)
	{
		int nxs = s;
		if(!(z&&0==i))
		{
			int n = to3(s);
			if(0 == buf[i] || 2 == buf[i]) buf[i] = 1;
			else buf[i] = 2;
			n = max(n,i+1);
			nxs = to10(buf,n);
		}
		sum += dfs(len-1,nxs,ismax&&i==mx,z&&0==i);
	}
	if(!ismax)
	{
		ans = sum;
		vis[len][s] = true;
	}
	return sum;
}
LL sloved(LL n)
{
	int len = 0;
	while(n)
	{
		bit[++len] = n%10;
		n /= 10;
	}
	return dfs(len,0,1,1);
}
int main()
{
#ifdef SHY
	freopen("d:\\1.txt", "r", stdin);
#endif
	int t;
	scanf("%d",&t);
	clc(vis,0);
	while(t--)
	{
		LL x,y;
		scanf("%llu %llu", &x, &y);
		printf("%llu\n", sloved(y)-sloved(x-1));
	}
	return 0;
}

 

发表评论

邮箱地址不会被公开。 必填项已用*标注

请输入正确的验证码