[HDU6796] X Number

题目

点这里看题目。

分析

显然可以数位 DP 。

\(R\) 的位数比较小的时候,我们可以暴力搜索出所有数字的出现情况,然后进行 DP 。

但是当 \(R\) 很长的时候,状态的范围就会非非非常大,无法 DP 。

但是注意到另一个事实是:对于一个确定的数,我们并不需要知道它长什么样子,而只需要知道数字的出现相对次数和对应的 \(D\)

这意味着,某些数对于我们而言是相同的。

比如 499892322624 对于我们而言,就是相同的,因为数码的出现情况是相同的。

这提示我们,像这样的数可以直接归为一类来计算。换言之,我们可以对它们重编号

其实随便怎么编号都无所谓,为了方便,我们统一采取 " 字典序最大 " 的方法(注意我们的 \(D\) 也有可能会变化)。

比如 499892322624 就都相当于 988786 ,这样只计算一次就可以算出许多数的方案。

采用这个方法, DP 状态就能得到极大的优化。反映到实际运行上,就相当于是:

 T 飞了 => 差 1~2s 就能卡进时限了!

嗯 ...... 于是我们还是会 TLE 。

考虑继续优化。注意到,当我们选数不再受上界影响的时候,这个问题实际上就会变得简单一些。

因此,当选数没有上界时,我们就可以直接背包 + 组合数解决弱化的问题。

然后存在上界的时候,我们再进行常规的数位 DP 计算。

这样就可以跑过了。

本题一些有价值的点:

  1. 优化状态。本质上忽略数码本身,而只考虑数码的出现情况
  2. 特殊哈希方法。这里没有采用进制压缩,而是使用了 " 字典序最大 " 的方法,将同一类出现情况映射到同一个数上。
  3. 灵活应变。问题复杂的时候,使用正常的 DP ;而问题弱化的时候,就使用更快速的方法。

代码

#include <cstdio>
#include <cstring>
#include <utility>
using namespace std;

typedef long long LL;
typedef pair<LL, int> pii;

#define int LL

const int mod = 501157, MAXN = 1e6 + 5;

template<typename _T>
void read( _T &x )
{
	x = 0;char s = getchar();int f = 1;
	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
	if( 9 < x ){ write( x / 10 ); }
	putchar( x % 10 + '0' );
}

LL pw[20];
int upper[20], tmp[20];
int C[20][20];
int D;

struct HashTable
{
	pii rVal[MAXN];
	int ans[MAXN][10];
	int head[mod], nxt[MAXN];
	int siz = 0;
	
	LL DP( LL num, const int lef, const int d )
	{
		LL ret = 0;
		LL f[11][20] = {}; int cnt[10] = {};
		while( num ) cnt[num % 10] ++, num /= 10;
		for( int t = 0 ; t <= lef ; t ++ )
		{
			memset( f, 0, sizeof f );
			f[10][0] = C[lef][t];
			for( int i = 9 ; ~ i ; i -- )
			{
				if( i == d )
				{
					for( int j = 0 ; j <= lef ; j ++ )
						f[i][j] = f[i + 1][j];
				}
				else
				{
					for( int j = 0 ; j <= lef - t ; j ++ )
						for( int k = 0 ; k <= lef - j - t && k + cnt[i] < t + cnt[d] ; k ++ )
							f[i][j + k] += f[i + 1][j] * C[lef - j - t][k];
				}
			}
			ret += f[0][lef - t];
		}
		return ret;
	}
	
	LL query( LL num, const int lef, const int d )
	{
		pii key( num, lef );
		int HASH = ( num % mod * 20 % mod + lef ) % mod;
		for( int i = head[HASH] ; i ; i = nxt[i] )
			if( key == rVal[i] )
				return ~ ans[i][d] ? ans[i][d] : ( ans[i][d] = DP( num, lef, d ) );
		int cur = ++ siz;
		memset( ans[cur], -1, sizeof ans[cur] );
		nxt[cur] = head[HASH], rVal[cur] = key, head[HASH] = cur;
		return ans[cur][d] = DP( num, lef, d );
	}
}T;
 
void init()
{
	pw[0] = 1;
	for( int i = 1 ; i <= 18 ; i ++ )
		pw[i] = pw[i - 1] * 10;
	for( int i = 0 ; i < 20 ; i ++ )
	{
		C[i][0] = C[i][i] = 1;
		for( int j = 1 ; j < i ; j ++ )
			C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
	}
}

int split( LL num, int *ret )
{
	int len = 0;
	for( int i = 0 ; i < 20 ; i ++ ) ret[i] = 0;
	while( num ) ret[len ++] = num % 10, num /= 10;
	return len;
}

LL DFS( const int ind, LL val, const bool up, const int D )
{
	LL ret = 0;
	if( ind < 0 || ! up )
	{
		if( ! val )
		{
			if( ind < 0 ) return D == 0;
			for( int i = 0 ; i < 10 ; i ++ )
				ret += DFS( ind - 1, val + pw[ind] * i, false, D );
		}
		else
		{
			int mp[10]; memset( mp, -1, sizeof mp ); mp[0] = 0;
			for( int i = 0 ; i <= ind ; i ++ ) val /= 10;
			int L = split( val, tmp ), dig = 9; LL nw = 0;
			for( int i = L - 1 ; ~ i ; i -- )
			{
				if( ! ( ~ mp[tmp[i]] ) ) mp[tmp[i]] = dig --;
				nw = nw + pw[i] * mp[tmp[i]];
			}
			for( int i = 9 ; ~ i ; i -- )
				if( ! ( ~ mp[i] )) mp[i] = dig --;
			ret = T.query( nw, ind + 1, mp[D] );
		}
		return ret;
	}
	for( int i = 0 ; i <= upper[ind] ; i ++ )
		ret += DFS( ind - 1, val + pw[ind] * i, i == upper[ind], D );
	return ret;
}

LL calc( const LL lim )
{
	int len = split( lim, upper );
	return DFS( len - 1, 0, true, D );
}

signed main()
{
	init();
	int T;
	LL L, R;
	read( T );
	while( T -- )
	{
		read( L ), read( R ), read( D );
		write( calc( R ) - calc( L - 1 ) ), putchar( '\n' );
	}
	return 0;
}
posted @ 2020-08-24 14:23  crashed  阅读(222)  评论(0编辑  收藏  举报