·Balanced Numbers SPOJ

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm> 
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
typedef long long LL;
const LL mod=1e9+7;
const int M=13;
using namespace std;
const int N =1e5+100;
LL dp[21][60000];//dp[pos][sta]
int v[20],a[21];//v[i]表示3^i
LL K(int x) { //判断状态是否满足题意
	//三进制表示
	//0表示未出现,1表示出现了奇数次,2表示出现了偶数次
	for(int i=0; x&&i<=9; i++,x/=3) {
		//如果没有出现过
		if(x%3==0)
			continue;
		//如果出现奇数次
		else if(x%3==1) {
			//如果这一位是奇数,那么应该出现偶数次
			//
			if(i&1)
				return 0;
			//为2,出现偶数次
		} else {
			//如果当数字是偶数
			if(i%2==0)
				return 0;
		}
	}
	return 1;
}
//当前状态,i的次数+1
int getsta(int sta,int i) {
	//前导0
	if(i==0&&sta==0)
		return 0;//排除前导0的影响
	int x=(sta%(v[i]*3))/v[i];//x为数字i出现的状态
	//sta%(v[i]*3) 表示把第i为以上的去掉
	//再/v[i] 表示出现了几次
	if(x<=1)
		return sta+v[i];
	return sta-v[i];
}
//sta 当前数字的每一位的三进制表示
LL dfs(int pos,int sta,int limit) { //数位dp板子
	//搜到最后
	if(pos==-1)
		return K(sta);
	//之前已经搜到过,没有限制
	if(!limit&&dp[pos][sta]!=-1)
		return dp[pos][sta];
	//限制
	int up=limit?a[pos]:9;
	LL ans=0;
	for(int i=0; i<=up; i++)
		//当前状态,i的次数+1
		ans+=dfs(pos-1,getsta(sta,i),limit&&i==up);
	return limit?ans:dp[pos][sta]=ans;
}
LL slove(LL x) {
	int pos=0;
	for(; x; x/=10)
		a[pos++]=x%10;
	return dfs(pos-1,0,1);
}
int main() {
	mem(dp,-1);
	v[0]=1;
	//三进制表示
	for(int i=1; i<=10; i++)
		v[i]=v[i-1]*3;
	int t;
	scanf("%d",&t);
	while(t--) {
		LL l,r;
		scanf("%lld%lld",&l,&r);
		printf("%lld
",slove(r)-slove(l-1));
	}
}
原文地址:https://www.cnblogs.com/QingyuYYYYY/p/12500372.html