数位DP Ⅱ

本节题目:数位之间和的关系。一般需要多加一维状态f[i][j][k]:表示长度为i,第一位是i,数位和等于k的数列个数

Seven Segment Display

题意:每个数字都有一个对应的权值,求l~r(十六进制数)之间所有数的所有数字的权值和

预处理数组有点麻烦,f[i][j]表示长度为i,第一个数是j的所有数列的数位之和的总和,f[i][j]=f[i-1][0~15]+j*(长度为i-1的数列个数即pow(16,i-1)).

dp枚举第i位的数字时统计时还要算上last*(长度为i-1的数列的个数pow(16,i-1)).

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=20;
LL f[N][N];
int s[]={6,2,5,5,4,5,6,3,7,6,6,5,4,5,5,4};
int power[20];
LL dp(LL n){
	if(n==0) return s[0]*8;
	vector<int> nums;
	for(int i=1;i<=8;++i){
		nums.push_back(n%16);
		n/=16;
	}
	LL res=0,last=0;
	for(int i=nums.size()-1;i>=0;--i){
		int x=nums[i];
		for(int j=0;j<x;++j){
			res+=f[i+1][j]+power[i]*last;
		}
		last+=s[x];
	}
	res+=last;
	return res;
}
int main(){
	power[0]=1;
	for(int i=1;i<=10;++i) power[i]=power[i-1]*16;
	int res=0,p=16;
	for(int i=0;i<16;++i) f[1][i]=s[i],res+=s[i];//cout<<res<<endl;
	for(int i=2;i<9;++i,p*=16){
		for(int j=0;j<16;++j){
			f[i][j]=s[j]*p;
			for(int k=0;k<16;++k){
				f[i][j]+=f[i-1][k];
			}
		}
	}
	res=0;
	int T;
	cin>>T;
	while(T--){
        LL n,m,t=0xFFFFFFFF;
	    scanf("%lld%llX",&n,&m);
        LL l=m;
        LL r=m+n-1;
        if(r>t)
            cout<<dp(t)+dp(r%(t+1))-(l==0?0:dp(l-1))<<endl;
        else cout<<dp(r)-(l==0?0:dp(l-1))<<endl;
	}
	return 0;
}

1084. 数字游戏 II

由于科协里最近真的很流行数字游戏。

某人又命名了一种取模数,这种数字必须满足各位数字之和 mod N 为 0。

现在大家又要玩游戏了,指定一个整数闭区间 [a.b],问这个区间内有多少个取模数。

输入格式

输入包含多组测试数据,每组数据占一行。

每组数据包含三个整数 a,b,N。

输出格式

对于每个测试数据输出一行结果,表示区间内各位数字和 mod N 为 0 的数的个数。

数据范围

1≤a,b≤2^31?1,

1≤N<100

输入样例:

1 19 9

输出样例:

2

f[i][j][k] 长度为i 第一位是i 数位和%m等于k。注意(少生孩子)多取模!

eg:(a-b+c)%c一样可能小于0 ,应该是((a-b)%c+c)%c

#include<bits/stdc++.h>
using namespace std;
const int N=20,M=110;
int f[N][N][M]; //f[i][j][k] 长度为i 第一位是i 数位和%m等于k
int mod(int x,int p){
    return (x%p+p)%p;
}
void init(int m){
    memset(f,0,sizeof f);
    for(int i=0;i<10;++i) f[1][i][i%m]++;
    for(int i=2;i<N;++i){
        for(int j=0;j<10;++j){
            for(int k=0;k<10;++k){
                for(int r=0;r<m;++r){
                    f[i][j][r]+=f[i-1][k][mod(r-j,m)];
                }
            }
        }
    }
}
int dp(int num,int m){
    if(num==0) return 1;
    vector<int> nums;
    while(num) nums.push_back(num%10),num/=10;
    int res=0,last=0;
    for(int i=nums.size()-1;i>=0;--i){
        int x=nums[i];
        for(int j=0;j<x;++j){
            res+=f[i+1][j][mod(m-last,m)];
        }
        last+=x;
        if(i==0&&last%m==0) res++;
    }
    return res;
}
int main(){
    int l,r,k;
    while(cin>>l>>r>>k){
        init(k);
        cout<<dp(r,k)-dp(l-1,k)<<endl;
    }

}

1086. 恨7不成妻

单身!

依然单身!

吉哥依然单身!

DS 级码农吉哥依然单身!

所以,他平生最恨情人节,不管是 214 还是 77,他都讨厌!

吉哥观察了 214 和 77 这两个数,发现:

2+1+4=7

7+7=7×2

77=7×11

最终,他发现原来这一切归根到底都是因为和 7 有关!

所以,他现在甚至讨厌一切和 7 有关的数!

什么样的数和 7 有关呢?

如果一个整数符合下面三个条件之一,那么我们就说这个整数和 7 有关:

整数中某一位是 7;

整数的每一位加起来的和是 7 的整数倍;

这个整数是 7 的整数倍。

现在问题来了:吉哥想知道在一定区间内和 7 无关的整数的平方和。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据占一行,包含两个整数 L 和 R。

输出格式

对于每组数据,请计算 [L,R] 中和 7 无关的数字的平方和,并将结果对 109+7 取模后输出。

数据范围

1≤T≤50,

1≤L≤R≤1018

输入样例:

3

1 9

10 11

17 17

输出样例:

236

221

0

我们要找的数需要满足三个性质,1.不含7 2.数字和%7不等于0 3.数%7不等于0,所以我们的预处理数组需要是个状态f[i][j][a][b]:长度为i,最高位是j,数字之和%7是a,数%7是b。f[i][j][a][b] 由\(f[i-1][0,1,2,3,4,5,6,8,9][a-j*10\)(i-1)\(][b-j]\)转移得到

然后这道题问的是所有数的立方和。假设我们枚举到第i位前面last,当前位枚举位j,那么就是立方和就是\(jA_1^2+jA_2^2+...+jA_t^2=j×10\)i-1\(^2×t+2×j×10\)\(i-1\)\(×(A_1+A_2+...+A_t)+(A_1^2+A_2^2+...+A_t^2)\)

所以需要维护三个值,即\(t=s0=A_1^0+A_2^0+...+A_t^0\),\(s1=A_1^1+A_2^1+...+A_t^1\),\(s2=A_1^2+A_2^2+...+A_t^2\)

\(s0\)直接统计个数即可求,\(s1=jA_1+jA_2+...+jA_t=j×10\)\(i-1\)\(×t+(A_1^1+A_2^1+...+A_t^1)\)

注意(少生孩子)多取模!

#include<bits/stdc++.h>
using namespace std;
const int N=21;
const int P=1e9+7;
#define int long long
typedef long long LL;
struct F{
    int s0,s1,s2;
}f[N][N][N][N];
int mod(int x,int y){
    return (x%y+y)%y;
}
int power7[N],power9[N];
void init(){
    for(int i=0;i<10;++i){
            if(i==7) continue;
            auto& v =f[1][i][i%7][i%7];
            v.s0++;v.s1+=i;v.s2+=i*i%P;
            v.s0%=P;v.s1%=P;v.s2%=P;
    }
    LL power=10;
    for(int i=2;i<N;++i,power*=10){
        for(int j=0;j<10;++j){
            if(j==7) continue;
            for(int a=0;a<7;++a){
                for(int b=0;b<7;++b){
                    for(int k=0;k<10;++k){
                        if(k==7) continue;
                        auto& v1=f[i][j][a][b],v2=f[i-1][k][mod(a-j*power,7)][mod(b-j,7)];
                        v1.s0=mod(v1.s0+v2.s0,P);
                        v1.s1=mod(v1.s1+(v2.s1+j*(power%P)%P*v2.s0%P)%P,P);
                        v1.s2= mod(v1.s2+(j*j%P*(power%P)%P*(power%P)%P*v2.s0%P
                            +2*j*(power%P)%P*v2.s1%P
                            +v2.s2),P);
                    }
                }
            }
        }
    }
    power7[0]=power9[0]=1;
    for(int i=1;i<N;++i){
        power7[i]=power7[i-1]*10%7;
        power9[i]=power9[i-1]*10%P;
    }
}
F get(int i,int j,int a,int b){
    int ss0,ss1,ss2; ss0=ss1=ss2=0;
    for(int aa=0;aa<7;++aa){
        for(int bb=0;bb<7;++bb){
            if(aa==a||bb==b) continue;
            ss0+=f[i][j][aa][bb].s0;ss0%=P;
            ss1+=f[i][j][aa][bb].s1;ss1%=P;
            ss2+=f[i][j][aa][bb].s2;ss2%=P;
        }
    }//cout<<i<<" "<<j<<" "<<a<<" "<<b<<" "<<" "<<s0<<" "<<s1<<" "<<s2<<endl;
    return (F){ss0,ss1,ss2};
}
signed dp(LL t){
    if(t==0) return 0;
    LL tt=t;
    vector<int> nums;
    while(tt) nums.push_back(tt%10),tt/=10;
    LL last_a=0,last_b=0,res=0;
    for(int i=nums.size()-1;i>=0;--i){
        int x=nums[i];
        for(int j=0;j<x;++j){
            if(j==7) continue;
            int a=mod(-last_a*power7[i+1],7),b=mod(-last_b,7);
            auto it=get(i+1,j,a,b);
            int s0=it.s0,s1=it.s1,s2=it.s2;
           // cout<<1+i<<" "<<j<<" "<<a<<" "<<b<<" "<<s0<<" "<<s1<<" "<<s2<<" "<<power9[i+1]<<" "<<last_a<<endl;
            res=mod(res+((last_a%P)*(power9[i+1]%P)%P*(last_a%P)%P*(power9[i+1]%P)%P*s0%P
                +2*(last_a%P)*(power9[i+1]%P)%P*s1%P
                +s2),P);
        }

        if(x==7) break;
        last_a=last_a*10+x;
        last_b+=x;
        if(i==0&&last_b%7&&last_a%7) res=mod(res+((t%P)*(t%P))%P,P);//cout<<t<<" "<<res<<endl;
    }
    return res%P;
}
signed main(){
    init();
    int T;
    cin>>T;
    while(T--){
        int a,b;
        cin>>a>>b;
        cout<<mod(dp(b)-dp(a-1),P)<<endl;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/jjl0229/p/12660497.html

时间: 2024-10-19 08:50:45

数位DP Ⅱ的相关文章

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

51nod1043(数位dp)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1043 题意:中文题诶- 思路:数位dp 我们用dp[i][j]来存储长度为2*i且一半和为j的所有情况(包括前导0的情况),为了方便我们现在只讨论其一半的和的情况,因为如果包括前导0的话其两边的情况是一样的: 我们假设再长度为i-1的数字最前面加1位数字k,0<=k<=9(这位数字加在哪里并不影响答案,因为我们在计算i-1长度的时候已经计算了所有组合情况,

数位dp

1.[hdu3709]Balanced Number 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<ctime> 8 #include<cmath> 9 #include<queue>

【HDU 3652】 B-number (数位DP)

B-number Problem Description A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string "13" and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task

hdu 5898 odd-even number 数位DP

odd-even number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 716    Accepted Submission(s): 385 Problem Description For a number,if the length of continuous odd digits is even and the length

LightOJ1068 Investigation(数位DP)

这题要求区间有多少个模K且各位数之和模K都等于0的数字. 注意到[1,231]这些数最大的各位数之和不会超过90左右,而如果K大于90那么模K的结果肯定不是0,因此K大于90就没有解. 考虑到数据规模,数据组数,这题状态这么表示: dp[i][j][k]:位数为i模K结果为j且各位数之和模K结果为k的数字个数 然后就是转移方程,最后就是统计.. 统计部分好棘手...半乱搞下AC的..还是对数位DP的这一部分太不熟悉了. 1 #include<cstdio> 2 #include<cstr

HDU 4734 F(x)(数位DP)

Description For a decimal number x with n digits (A nA n-1A n-2 ... A 2A 1), we define its weight as F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A1 * 1. Now you are given two numbers A and B, please calculate how many numbers are there betwe

hdu 4734 数位dp

http://acm.hdu.edu.cn/showproblem.php?pid=4734 Problem Description For a decimal number x with n digits (AnAn-1An-2 ... A2A1), we define its weight as F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1. Now you are given two numbers A and B, plea

【POJ3208】传说中POJ最难的数位DP?(正解AC自动机,二解数位DP,吾异与之)

题意: 多组数据,每组求第n个包含'666'的数(不能断开),如1:666,2:1666,14:6667. 题解: AC自动机解法没去想,数位DP没学,这里有一种类似于数位DP,却又与数位DP不同,我称为数位树. 数位树: 将数n如线段树一样地拆分成多个小段,进行递归处理得出答案. 本题详(lue)解: 直接看每一位应该是什么数,然后n减去相应的数,使得在下一层转换为子问题"在开头有b个连续的6时,求第a个带'666'的数".就是如此简单,如此简单!!!! 代码来啦! #include