zoukankan      html  css  js  c++  java
  • 【ybt高效进阶5-3-1】B数计数(数位DP)

    B数计数

    题目链接:ybt高效进阶5-3-1

    题目大意

    要你求 1~n 这些数有多少个数是 13 的倍数,而且它字符串包含 13 这个子串。

    思路

    不难想到这些题可以用数位 DP 来做。

    然后不难想到设 (f_{i,j,k}) 为从高位到低位填到了 (i) 位,然后当前余数是 (j),字符串包含情况时 (k)
    如果已经有 (13) 出现,则 (k=2),如果前面一个是 (1)(k=1),否则 (k=0)

    然后就先枚举 (i),然后这一次的数,然后前面的余数,那我们就可以算出这次的余数。
    然后就是分类讨论一下,分别讨论 (k=1,2,3) 的。

    然后我们考虑如何求 (1sim n) 中有多少个满足的。
    不难想到有这样一个方法,当我们确定剩下的可以随便选的时候,那我们就可以直接用我们预处理出来的 (f) 数组。
    那我们假设 (n)(x_1x_2x_3x_4),那当 (0leqslant i< x_1) 的时候,我们 (ixxx) 就可以随便选。
    那当 (i=x_1) 的时候,我们就要继续看下一位。

    那不难发现一个问题,在个位它只会处理到最后倒数第二个,最后一个数它不会搞。
    那也就是说,它求的是 (1sim n-1) 的。
    那我们把 (n+1) 丢进去查询不久好了吗。

    代码

    #include<cstdio>
    
    using namespace std;
    
    int n, f[11][15][5], a[11], sz, mi[10];
    int nowmo;
    
    void DP() {//预处理 DP
    	mi[0] = 1;
    	for (int i = 1; i <= 9; i++) mi[i] = mi[i - 1] * 10;
    	
    	f[0][0][0] = 1;
    	for (int ws = 1; ws <= 10; ws++) {
    		for (int num = 0; num <= 9; num++)
    			for (int bef_k = 0; bef_k < 13; bef_k++) {
    				int now_k = (bef_k + num * mi[ws - 1]) % 13;
    				if (num != 3) f[ws][now_k][0] += f[ws - 1][bef_k][0];
    				if (num != 1 && num != 3) f[ws][now_k][0] += f[ws - 1][bef_k][1];
    				//最终构不出 13
    				
    				if (num == 3) f[ws][now_k][1] += f[ws - 1][bef_k][1] + f[ws - 1][bef_k][0];
    				//可以有一个 3
    				
    				if (num == 1) f[ws][now_k][2] += f[ws - 1][bef_k][1];
    				f[ws][now_k][2] += f[ws - 1][bef_k][2];
    				//原本就有 13 或新构出了 13
    			}
    	}
    }
    
    int work(int now, int op) {
    	if (now < 1) return 0;
    	int re = 0, newop = 0;
    	for (int i = 0; i < a[now]; i++) {//可以搞的就直接全部一起搞(用 DP)
    		int nm = (13 - (nowmo + i * mi[now - 1]) % 13) % 13;
    		if (op == 2 || (op == 1 && i == 3)) newop = 2;
    			else if (i == 1) newop = 1;
    				else newop = 0;
    		if (newop) re += f[now - 1][nm][1];
    		if (newop == 2) re += f[now - 1][nm][0];
    		re += f[now - 1][nm][2];
    	}
    	
    	if (op == 2 || (op == 1 && a[now] == 3)) newop = 2;
    		else if (a[now] == 1) newop = 1;
    			else newop = 0;
    	
    	nowmo = (nowmo + a[now] * mi[now - 1]) % 13;
    	return re + work(now - 1, newop);//剩下的这一位只能搞一些,要通过这个继续搞
    }
    
    int main() {
    	DP();
    	
    	while (scanf("%d", &n) != EOF) {
    		n++;
    		sz = 0;
    		int tmp = n;
    		while (tmp) {
    			sz++;
    			tmp /= 10;
    		}
    		tmp = n;
    		for (int i = sz; i >= 1; i--) {
    			a[sz - i + 1] = tmp % 10;
    			tmp /= 10;
    		}
    		
    		nowmo = 0;
    		printf("%d
    ", work(sz, 0));
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    Elasticsearch
    区块链 blockchain
    IM协议
    MQ,互联网架构解耦神器
    服务中的 API 网关(API Gateway)
    OSI七层与TCP/IP五层网络架构详解
    JQ input标签限制输入数字或字母
    c:forEach用法
    jquery在线引用
    JSONObject使用方法
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_GXJJ_5-3-1.html
Copyright © 2011-2022 走看看