HDU多校第六场 1011 11 Dimensions —— DP +思维

题目链接:点我啊╭(╯^╰)╮

题目大意:

    长度为 n n 的数,部分位置是 可以是 0 0 ~ 9 9
    要求这个数整除 m m q q 次询问
    每次询问第 k k 小的解

解题思路:

    对于 23 ? ? 56 ? ? 23??56??
    将其拆为 23005600 23005600 + + ? ? 00 ? ? ??00??
    然后考虑 的方案, c n t cnt 个问号的方案数为 1 0 c n t 10^{cnt}
    因为 k 1 0 18 k≤10^{18} ,如果 1 0 c n t 10^{cnt} 内的数均匀分布,整除 m m 的个数大约为 1 0 c n t m \frac{10^{cnt}}{m}
    因此 c n t = 20 cnt = 20 即可,也就是探讨后 20 20 个问号的方案,前面的问号都取为 0 0


     d p [ i ] [ j ] dp[i][j] 为到第 i i 个问号,模 m m j j 的方案数
     d p [ i ] [ j ] = d p [ i 1 ] [ j + 1 0 i × x ] dp[i][j] = \sum dp[i-1][j + 10^i \times x]    x [ 0 , 9 ] x ∈ [0,9]
    注意 i = 1 i=1 的时候,要将初始的影响加上去,也就是 23005600 23005600
    可以理解为用第一位去解决初始的影响,用高位的话不是最贪心的
    查询的时候从高位到低位枚举,也要考虑到第一位时初始的影响
    时间复杂度: O ( 20 m 10 + q 20 10 ) O(20*m*10 + q*20*10)

核心:DP + 思维

#include<bits/stdc++.h>
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 5e4 + 5;
const ll mod = 1e9 + 7;
int T, n, m, q;
char s[maxn];
ll dp[30][30], fac1[50], fac2[50];

int main() {
	scanf("%d", &T);
	while(T--) {
		scanf("%d%d%d%s", &n, &m, &q, s);
		ll p1 = 1, p2 = 1, val = 0, rem = 0, cnt = 0, k;
		for(int i=n-1; ~i; i--) {
			if(isdigit(s[i])) {
				val = (val + (s[i]-'0') * p1 % mod) % mod;
				rem = (rem + (s[i]-'0') * p2 % m) % m;
			} else if(cnt < 20) {
				fac1[++cnt] = p1;
				fac2[cnt] = p2;
			}
			p1 = p1 * 10 % mod;
			p2 = p2 * 10 % m;
		}
		memset(dp, 0, sizeof(dp));
		dp[0][0] = 1;
		for(int i=1; i<=cnt; i++)
			for(int j=0; j<m; j++)
				for(int x=0; x<10; x++) {
					if(i > 1) dp[i][j] += dp[i-1][(j + fac2[i] * x % m) % m];
					else dp[i][j] += dp[i-1][(j + rem + fac2[i] * x % m) % m];
					if(dp[i][j] >= 1e18 + 10 || dp[i][j] < 0) dp[i][j] = 1e18 + 10;
				}
				
		while(q--) {
			scanf("%lld", &k);
			if(dp[cnt][0] < k) {
				puts("-1");
				continue;
			}
			ll ans = val, pre_rem = 0, now_rem;
			for(int i=cnt; i; i--)
				for(int x=0; x<10; x++) {
					if(i > 1) now_rem = (pre_rem + fac2[i] * x % m) % m;
					else now_rem = (pre_rem + rem + fac2[i] * x % m) % m;
					
					if(dp[i-1][now_rem] < k) k -= dp[i-1][now_rem];
					else {
						pre_rem = now_rem;
						ans = (ans + fac1[i] * x % mod) % mod;
						break;
					}
				}
			printf("%lld\n", ans);
		}
	}
}
发布了221 篇原创文章 · 获赞 220 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Scar_Halo/article/details/103102386