zoukankan      html  css  js  c++  java
  • [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( '
    ' );
    	}
    	return 0;
    }
    
  • 相关阅读:
    request.getParameter() 、 request.getInputStream()和request.getReader() 使用体会
    HTTP之Content-Length
    关于spring3中No Session found for current thread!and Transaction的配置和管理(转)
    Java数据类型和MySql数据类型对应一览
    Spring MVC 解读——View,ViewResolver(转)
    LeetCode 441. Arranging Coins
    LeetCode 415. Add Strings
    LeetCode 400. Nth Digit
    LeetCode 367. Valid Perfect Square
    LeetCode 326. Power of Three
  • 原文地址:https://www.cnblogs.com/crashed/p/13553651.html
Copyright © 2011-2022 走看看