Jzoj P4239 光棍___数位dp

版权声明:欢迎借鉴,谢绝抄搬。 https://blog.csdn.net/Gx_Man_VIP/article/details/86697020

题目大意:

给出 T T 个询问,
每个询问给出一组 ( L , R , A ) (L,R,A) ,求一些在区间 [ L , R ] [L,R] 的数,满足:
1. A 1.不是A的倍数
2. A 2.数位之和不是A的倍数
3. A 3.数位中任意一位都不为A
问这些数分别平方后的和对 1 e 9 + 7 1e9+7 取模的结果是多少。
一切在十进制下进行。
0 < = L < = R < = 1 0 18 , 2 < = A < = 9 , T < = 100 0 <= L <= R <= 10^{18},2 <= A <= 9, T <= 100

分析:

很毒瘤的一道题,细节调到心态爆炸
容斥一下可以发现,
对于一组询问 ( L , R , A ) (L,R,A)
答案为可以被表示为 [ 1 , R ] [ 1 , L ] 贡献_{[1,R]}-贡献_{[1,L]} ,然后这题就好做很多了
可以使用数位 d p dp
假设我们要求 [ 1 , x ] [1,x] 的贡献,
d p i , j , k , 0 / 1 , 0 / 1 , 0 / 1 / 2 dp_{i,j,k,0/1,0/1,0/1/2} 表示选到了第 i i 位(从高位开始选),满足自身 m o d mod A = j A=j ,所有数位之和 m o d mod A = k A=k , 数位中是否出现过 A A (出现为 1 1 ,否则为 0 0 ),是否满位(即与 x x 的前 i i 个高位是否相等,相等为 1 1 ,否则为 0 0 ),最后的 0 / 1 / 2 0/1/2 分别表示这样的数的个数,以及它们的和与它们的平方和。
n u m [ i ] num[i] 表示数 x x 的第 i i 个高位的数值
那么初值:
枚举第一个高位的数值 i i ,
d p 1 , i m o d A , i m o d A , ( i = A ) , ( i = n u m [ 1 ] ) , 0 dp_{1,imodA,imodA,(i=A),(i=num[1]),0} ,对应位置 + 1 +1
d p 1 , i m o d A , i m o d A , ( i = A ) , ( i = n u m [ 1 ] ) , 1 dp_{1,imodA,imodA,(i=A),(i=num[1]),1} ,对应位置 + i +i
d p 1 , i m o d A . i m o d A , ( i = A ) , ( i = n u m [ 1 ] ) , 2 dp_{1,imodA.imodA,(i=A),(i=num[1]),2} ,对应位置 + i 2 +i^2
( a = b ) (a=b) 运算: a = b a=b 时返回 1 1 ,否则返回 0 0
然后我们思考一下如何转移:
假设我们已经计算了第 i i 个高位的 d p i , j , k , 0 / 1 , 0 / 1 , 0 / 1 / 2 dp_{i,j,k,0/1,0/1,0/1/2}
那么我们考虑如何找到与第 i + 1 i+1 个高位的关系,
枚举 j j k k l l ,为
选到了第 i i 位,满足数 m o d mod A = j A=j ,数位之和 m o d mod A = k A=k A A 的出现状态为 l l 一类的数
①前 i i 位中并没有满位,此时第 i + 1 i+1 位可以随便选也不会出现满位的情况,
那么枚举第 i + 1 i+1 位的选数情况,即 0   9 0~9 ,设当前选 w q wq
则可以转移:
d p i + 1 , ( j + w q ) m o d A , ( k 10 + w q ) m o d A , l ( w q = A ) , 0 , 0 dp_{i + 1,(j + wq) modA,(k*10+wq) modA,l||(wq=A),0,0} += d p i , j , k , l , 0 , 0 dp_{i,j,k,l,0,0}
d p i + 1 , ( j + w q ) m o d A , ( k 10 + w q ) m o d A , l ( w q = A ) , 0 , 1 dp_{i + 1,(j + wq) modA,(k * 10+wq)modA,l || (wq = A),0,1} += 10 d p i , j , k , l , 0 , 1 + 1 d p i , j , k , l , 0 , 0 w q ) 10 * dp_{i,j,k,l,0,1} + 1 * dp_{i,j,k,l,0,0} * wq)
d p i + 1 , ( j + w q ) m o d A , ( k 10 + w q ) m o d A , l ( w q = A ) , 0 , 1 dp_{i + 1,(j + wq) modA,(k * 10+wq)modA,l || (wq = A),0,1} += 100 d p i , j , k , l , 0 , 2 + 20 d p i , j , k , l , 0 , 1 w q + 1 d p i , j , k , l , 0 , 0 w q 2 ) 100 * dp_{i,j,k,l,0,2} + 20 * dp_{i,j,k,l,0,1} * wq + 1 * dp_{i,j,k,l,0,0} * wq^2)
②前 i i 位满位了,此时第 i + 1 i+1 位选 [ 0 , n u m [ i + 1 ] 1 ] [0,num[i+1]-1] 时候不会满位,选 n u m [ i + 1 ] num[i+1] 时会满位,
那么只需要将转移中的满位情况求出即可,转移同
最后的 [ 1 , x ] [1,x] 的贡献就是 i = 1 A 1 j = 1 A 1 k = 0 1 d p l e n , i , j , 0 , k , 2 \sum_{i=1}^{A-1}\sum_{j=1}^{A-1}\sum_{k=0}^{1}dp_{len,i,j,0,k,2} ,len为 x x 的位数
注意过程中取模以及取模会出现的负数情况即可。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

const int modn = 1000000007;

ll dp[20][10][10][2][2][3], num[20], L, R, A;
int T;

void read(ll &x)
{
    ll f = 1; x = 0; char s = getchar();
	while (s < '0' || s > '9')   { if (s == '-') f = - 1;  s = getchar(); }
	while (s >= '0' && s <= '9') { x = x * 10 + (s - '0'); s = getchar(); }	
    x = x *f;
}

ll solve(ll x)
{
	if (x == - 1 || x == 0) return 0;
	ll cdp = 0; int len = 0;
	for (; x; x /= 10) num[++len] = x % 10;
	for (int i = 1; i <= len / 2; i++) swap(num[i], num[len - i + 1]);
	memset(dp, 0, sizeof(dp));
	for (int i = 0; i <= num[1]; i++)
	{
		dp[1][i % A][i % A][(i == A)][(i == num[1])][0] += 1;
		dp[1][i % A][i % A][(i == A)][(i == num[1])][1] += i;
		dp[1][i % A][i % A][(i == A)][(i == num[1])][2] += i * i;
	}
	// 位数 / 数位的和%A /  数的大小%A / 是否出现过A / 是否满位 / 个数,和,平方和 
	for (int i = 1; i < len; i++)
	    for (int j = 0; j < A; j++)
	        for (int k = 0; k < A; k++)
	            for (int l = 0; l <= 1; l++)
				{
		            for (int wq = 0; wq <= 9; wq++)
		            {
			            dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][0] = 
						  (dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][0] + dp[i][j][k][l][0][0]) % modn;
			            
						dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][1] = 
						  (dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][1] + 10LL * dp[i][j][k][l][0][1] + 1LL * dp[i][j][k][l][0][0] * wq) % modn;
			            
						dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][2] = 
						  (dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][2] + 100LL * dp[i][j][k][l][0][2] + 20LL * dp[i][j][k][l][0][1] * wq + 1LL * dp[i][j][k][l][0][0] * wq * wq) % modn;		
		            }
		            for (int wq = 0; wq <= num[i + 1]; wq++)
					{
			            dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][0] = 
						  (dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][0] + dp[i][j][k][l][1][0]) % modn;
			            dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][1] =
						  (dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][1] + 10LL * dp[i][j][k][l][1][1] + 1LL * dp[i][j][k][l][1][0] * wq) % modn;
			            dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][2] =
						  (dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][2] + 100LL * dp[i][j][k][l][1][2] + 20LL * dp[i][j][k][l][1][1] * wq + 1LL * dp[i][j][k][l][1][0] * wq * wq) % modn;		
					}
                }
	// 位数 / 数位的和%A /  数的大小%A / 是否出现过A / 是否满位 / 个数,和,平方和 
	for (int i = 1; i < A; i++)	
	    for (int j = 1; j < A; j++)
	        for (int k = 0; k <= 1; k++) cdp = (cdp + dp[len][i][j][0][k][2]) % modn;
	return cdp;
}
int main()
{
	scanf("%d", &T);
	while(T--)
	{
		read(L); read(R); read(A);
		printf("%lld\n" , ((solve(R) - solve(L - 1)) % modn + modn) % modn);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Gx_Man_VIP/article/details/86697020