zoukankan      html  css  js  c++  java
  • Luogu P5842 【[SCOI2012]Blinker 的仰慕者】

    Description

    传送门


    Solution

    不难想到这题用数位(dp)解决。

    那么首先可以想到(dp_{i, j, num2, num3, num5, num7})表示从高到低填了(i)位,是否卡位,乘积中的每个质因子数量是多少此时的方案数,(sum_{i, j, num2, num3, num5, num7})表示这些数的和。

    这样子的转移是很好想的不再赘述,但是这样定义状态空间会爆炸,大概需要(log_2 {2 ^ {64}} imes log_3 {2 ^ {64}} imes log_5 {2 ^ {64}} imes log_7 {2 ^ {64}} imes 2 imes 19 imes 2= 125296640)的空间。

    这时注意到实际上没必要用这么多状态,所有可行的状态只有大概(66061)个,那么我们可以把所有的可行状态都扔到一个哈希表里,就解决了空间的问题。

    接下来就可以按照套路先预处理出(f_{i, j})表示从低到高(i)位,乘积在哈希表里的位置是(j)的方案数,(sum_{i, j})表示此时的和。

    剩下的就是如何卡位以及算贡献。

    从高位到低位枚举一下(lcp)然后枚举这位填的数字(j)。先判断填(j)是否可行,也就是说看看(lcp)(j)的积是否整除(k)。只要这一位不是填的最高位那么后面可以随便填,所以这一位如果可行就在最终的答案里加上((lcp imes 10 + j) * 10^{i - 1} * f_{i - 1, k / lcp / j} + g_{i - 1, k / lcp / j})

    最后别忘了加上(sum_{i = 1}^{n - 1}g_{i, k})

    对于卡位,每一位都卡到最高数字要求只算一遍贡献。我们每次枚举每一位填什么的时候填小于当前位的数字,这时我们发现所有位都卡位的情况没有算上,那么可以将要算的数先加一,这样保证只在最后一位的时候算上了贡献。

    注意(k)的取值可能是(0),这是我们就要求至少有一位是(0)并且有数字出现过,这就是一个基础的数位(dp),此处不再赘述。


    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int MOD = 20120427, HashMOD = 1000037, zt = 66062;
    
    #define ll long long
    
    int head[HashMOD + 50], num, f[19][zt], g[19][zt], cnt, zhi[20], pow[19], dp[20][2][2][2], sum[20][2][2][2];
    
    struct Node
    {
    	int next;
    	ll zhi;
    } hash[HashMOD + 50];
    
    template <class T>
    void Read(T &x)
    {
    	x = 0; int p = 0; char st = getchar();
    	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
    	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
    	x = p ? -x : x;
    	return;
    }
    
    template <class T>
    void Put(T x)
    {
    	if (x < 0) putchar('-'), x = -x;
    	if (x > 9) Put(x / 10);
    	putchar(x % 10 + '0');
    	return; 
    }
    
    int Ins(ll x)
    {
    	int bh = x % HashMOD;
    	for (int i = head[bh]; i; i = hash[i].next)
    		if (hash[i].zhi == x) return i;
    	hash[++num] = (Node){head[bh], x};
    	head[bh] = num;
    	return num;
    } 
    
    int Ask(ll x)
    {
    	int bh = x % HashMOD;
    	for (int i = head[bh]; i; i = hash[i].next)
    		if (hash[i].zhi == x) return i;
    	return 0;
    }
    
    void Prework()
    {
    	pow[0] = 1;
    	for (int i = 1; i <= 18; i++) pow[i] = (pow[i -1] * 10) % MOD;
    	int tmp;
    	f[0][Ins(1)] = 1;
    	for (int i = 0; i < 18; i++)
    		for (int j = 1; j <= num; j++)
    			for (int k = 1; k <= 9; k++)
    			{
    				if (hash[j].zhi * k > 1000000000000000000LL) break;
    				(f[i + 1][tmp = Ins(hash[j].zhi * k)] += f[i][j]) %= MOD,
    				(g[i + 1][tmp] += g[i][j] * 10 % MOD + f[i][j] * k) %= MOD;
    			}
    	return;
    } 
    
    int Work(ll x, ll k)
    {
    	cnt = 0;
    	while (x)
    	{
    		zhi[++cnt] = x % 10;
    		x /= 10;
    	}
    	ll ans = 0, pre = 0, mul = 1;
    	for (int i = cnt; i; i--)
    	{
    		if (!zhi[i]) break;
    		for (int j = 1; j < zhi[i]; j++)
    			if ((k % (mul * j)) == 0)
    			{
    				ll tmp = k / mul / j;
    				int t = Ask(tmp);
    				(ans += (pre * 10 + j) * pow[i - 1] % MOD * f[i - 1][t] % MOD + g[i - 1][t]) %= MOD; 
    			}
    		pre = pre * 10 + zhi[i];
    		mul = mul * zhi[i];
    	}
    	int tmp = Ask(k);
    	for (int i = 1; i < cnt; i++) (ans += g[i][tmp]) %= MOD;
    	return ans;
    } 
    
    int Work0(ll x)
    {
    	cnt = 0;
    	memset(dp, 0, sizeof(dp));
    	memset(sum, 0, sizeof(sum));
    	while (x)
    	{
    		zhi[++cnt] = x % 10;
    		x /= 10;
    	}
    	dp[cnt + 1][1][0][0] = 1;
    	for (int i = cnt + 1; i >= 2; i--)
    	{
    		if (i != cnt + 1) dp[i][0][0][0] = 1;
    		for (int j = 0; j <= 1; j++)
    			for (int t = 0; t <= 1; t++)
    				for (int q = 0; q <= 1; q++)
    					if (dp[i][j][t][q])
    						for (int k = 0; k <= 9; k++)
    						{
    							if (j && k > zhi[i - 1]) break;
    							if (!t && !q && !k) continue; 
    							(dp[i - 1][j && (k == zhi[i - 1])][t || (!k)][q || k] += dp[i][j][t][q]) %= MOD; 
    							(sum[i - 1][j && (k == zhi[i - 1])][t || (!k)][q || k] += sum[i][j][t][q] * 10 + dp[i][j][t][q] * k) %= MOD;
     						}
    	}
     	return (sum[1][1][1][1] + sum[1][0][1][1]) % MOD;
    }
    
    int main()
    {
    	Prework();
    	int t;
    	ll l, r, k;
    	Read(t);
    	while (t--)
    	{
    		Read(l); Read(r); Read(k);
    		if (k) Put((Work(r + 1, k) - Work(l, k) + MOD) % MOD), putchar('
    ');
    		else Put((Work0(r) - Work0(l - 1) + MOD) % MOD), putchar('
    ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    GIT初探
    IIS7添加虚拟目录映射另一台服务器的共享文件夹
    The server committed a protocol violation. Section=ResponseHeader Detail=CR must be followed by LF
    asp.net编码解码的两篇文章
    生成网站时提示 已预编译此应用程序
    错误 1 无法将程序集“NBear.Data.dll”复制到文件“D: ewbpmpmSureBpmBinNBear.Data.dll”。无法将“D: ewbpmpmSureSoft.WebServiceBaseLibinDebugNBear.Data.dll”添加到网站。 无法添加文件“BinNBear.Data.dll”。 拒绝访问。 D:..
    从Script到Code Blocks、Code Behind到MVC、MVP、MVVM(转载)
    HTML form表单的默认提交方式
    webapp检测手机运动方向,可实现手机摇一摇功能的触发
    手机/P各种提示框整合,纯js实现,比前端大多数框架的提示消息更好用
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13097801.html
Copyright © 2011-2022 走看看