看到诸位神牛的代码和Blog,我也来班门弄斧学一下 数位dp
Step0:找木板和资料
向ftiasch 和 edward_mj (窃笑,师父们T_T)求了资料,得到一个好板子——BUPT 某神的Blog
Step1:撸水题
HDU 2089 直接暴力就可以,不过还是老老实实地数位dp一把,基本是板子题目。
HDU 3555 同上的数位DP
UESTC 1307 前导0 建立状态原来还可以用11来代替啊。这样的话每次Quest dfs一次即可。
POJ 3252 f[pos][s][zero][one]
Step2:撸中等题
1.hdu 4507
http://acm.hdu.edu.cn/showproblem.php?pid=4507
Tecent 2012.3.21 C 题
题目描述
求[l , r] 中
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
方法:
肯定是数位dp
必须这个满足减法——[l , r] = [0 , r] - [0 , l - 1]
我的算法是算相反的——即和数7有关的平方和,然后用1/6 * n * (n + 1) * (2n + 1) 来减一下
首先确定状态 —— dp[枚举位数][是否含有7][个数的和%7][这个数%7]
其次是维护:
1、个数
2、和 (由个数算出
3、平方和(由个数和 和 算出
首先说个数算法:
这就是一个最裸的数位dp,贴个模板就能做了 。。模板含义见资料
再说和的做法:(这里很容易错啊。。。)
在枚举第 i 位 是 d 的时候,算出 i - 1 位之后的个数 has ,那么这个d 就出现了 has 次,于是就要统计 d * 10的幂 * has 。
最后说平方和的算法:(卧槽这里写挂了好久好么。。。)
还是枚举第 i 位 是 d 的时候。我们看跟后面的关系。假设这个数是 dxxxxxxx(后面一大堆数字) 。假设这个数字是 d * 10的幂(设为 x) + y(后面一大堆数字),那么我们就是要计算(x + y)^2。 拆开来就是 x ^ 2 + 2xy + y ^ 2 。 首先 y ^ 2 在后面我们已经算过了,直接 搜索深度+1就可以计算,2xy 的话需要用 2 * x * (出现次数) * (后面数字的一次幂和), x ^ 2 的话直接 就 x ^ 2 乘以 后面的出现次数。
那么这个题目就解决了。。。
Debug方法:
卧槽数位dp debug 很关键好么。。
我这次的方法很科学,用个暴力的代码算出来比较坑爹的几个数字,比如 7 , 14 , 21 , 100 , 139 的三个部分 —— 个数 , 和, 平方和
然后,剥洋葱皮一样一点儿一点儿展开,先debug最外层的——个数,过掉几个数据之后说明可以信任;然后搞 和最后搞 平方和
Code
我的Code
LL cnt[20][2][7][7] , dp[20][2][7][7] , dpsum[20][2][7][7];
LL ten[20];
LL num[20];
void init(){
ten[0] = 1;
REP_1(i , 19) ten[i] = (ten[i - 1] * 10) % MOD;
}
LL dfscnt(int i, bool seven , int numbersum, int sum , bool e) {
if (i==-1) return (seven || numbersum % 7 == 0 || sum % 7 == 0) ? 1 : 0;
if (!e && ~cnt[i][seven][numbersum][sum]) return cnt[i][seven][numbersum][sum];
LL res = 0;
int u = e?num[i]:9;
for (int d = 0; d <= u; ++d)
res = ( res + dfscnt(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u)) % MOD;
return e?res:cnt[i][seven][numbersum][sum]=res;
}
LL mul(LL x , LL y){
x %= MOD;
y %= MOD;
return (x * y) % MOD;
}
LL dfssum(int i , int seven , int numbersum , int sum , bool e){
if (i == -1) return 0;
if (!e && ~dpsum[i][seven][numbersum][sum]) return dpsum[i][seven][numbersum][sum];
LL res = 0;
int u = e ? num[i] : 9;
for (int d = 0 ; d <= u ; ++d){
LL has = dfscnt(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u);
LL tmp = mul(d , ten[i]);
tmp = mul(has , tmp);
res = (res + dfssum(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u)) % MOD;
res = (res + tmp) % MOD;
}
return e ? res : dpsum[i][seven][numbersum][sum] = res ;
}
LL dfs(int i , int seven , int numbersum , int sum , bool e){
if (i == -1) return 0;
if (!e && ~dp[i][seven][numbersum][sum]) return dp[i][seven][numbersum][sum];
LL res = 0;
int u = e ? num[i] : 9;
for (int d = 0 ; d <= u ; ++d){
LL has = dfscnt(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u);
LL sum1 = dfssum(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u);
LL tmp = mul(d , ten[i]);
LL nownow = mul(tmp , tmp);
LL hasnow = mul(nownow , has);
nownow = mul(2 , mul(tmp , sum1));
res = (res + dfs(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u)) % MOD;
res = (res + hasnow) % MOD;
res = (res + nownow) % MOD;
}
return e ? res : dp[i][seven][numbersum][sum] = res ;
}
LL l , r;
LL gao(LL x){
LL a = x , b = x + 1 , c = 2 * x + 1;
LL p = 2;
if (a % p == 0) a /= p;
else if (b % p == 0) b /= p;
else if (c % p == 0) c /= p;
p = 3;
if (a % p == 0) a /= p;
else if (b % p == 0) b /= p;
else if (c % p == 0) c /= p;
return mul(mul(a , b) , c);
}
LL getans(LL x){
if (x == 0) return 0;
int s = 0;
LL y = x;
while(x){
num[s ++ ] = x % 10;
x /= 10;
}
x = y;
LL res = gao(x);
res -= dfs(s - 1 , 0 , 0 , 0 , 1);
res %= MOD;
res += MOD;
res %= MOD;
return res;
}
void solve(){
RD(l , r);
LL ans = getans(r) - getans(l - 1);
ans %= MOD;
ans += MOD;
ans %= MOD;
printf("%I64d\n" , ans);
}
int main(){
FLC(dp , -1);
FLC(cnt , -1);
FLC(dpsum , -1);
init();
Rush solve();
}
AC的(不是很懂。。。)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<string>
#include<iostream>
using namespace std;
typedef long long LL;
typedef double db;
#define mp make_pair
#define pb push_back
const LL P = (int)1e9 + 7;
LL f[20][9 * 20][7][2];//cnt
LL sum1[20][9 * 20][7][2];// sum = 1次和
LL sum2[20][9 * 20][7][2];//sum = 2次和
LL ten[100];
void update(LL &a, LL b) {
a = (a + b) % P ;
if(a<0) a+=P;
}
LL MOD(LL a) {
a %= P;
return a <0 ? a + P : a;
}
/*debug*/
LL _f[20][9 * 20][7][2];
LL _sum1[20][9 * 20][7][2];
LL _sum2[20][9 * 20][7][2];
void _pre(){
_f[0][0][0][0]= 1;
for(LL i = 1;i<=6;++i) {
LL lo = 0, hi = ten[i]-1;
for(LL v = lo; v <= hi; ++ v){
LL rem = v % 7, dsum = 0;
LL k = v,msk = 0;
while(k){
LL d = k%10;
if(d == 7) msk = 1;
dsum += d;k/=10;
}
_f[i][dsum][rem][msk]++;
update(_sum1[i][dsum][rem][msk] , v);
update(_sum2[i][dsum][rem][msk] , (LL)v*v%P);
}
}
for(LL i = 0; i <= 6; ++ i) {
LL sumlim = i * 9;
for(LL sum = 0; sum <= sumlim; ++ sum) {
for(LL modres = 0; modres < 7; ++ modres) {
for(LL has7 = 0; has7 < 2; ++ has7) {
if(_f[i][sum][modres][has7] != f[i][sum][modres][has7]) {
cout << "error at f!" << endl;
return;
}
if(_sum1[i][sum][modres][has7] != sum1[i][sum][modres][has7]) {
cout << "error at s1!" << endl;
cout << i <<" " << sum <<" " << modres << " " << has7 << endl;
cout <<_sum1[i][sum][modres][has7] <<" "<<sum1[i][sum][modres][has7]<<endl;
return;
}
if(_sum2[i][sum][modres][has7] != sum2[i][sum][modres][has7]) {
cout << "error at s2!" << endl;
cout <<_sum2[i][sum][modres][has7] <<" "<<sum2[i][sum][modres][has7]<<endl;
return;
}
}
}
}
}
cout <<"ok"<<endl;
}
LL ten7[100];
void pre(){
f[0][0][0][0] = 1;
ten[0] = 1; ten7[0]=1;
for(LL i = 1; i <= 99; ++ i) ten[i]=ten[i-1] * 10%P;
for(LL i = 1; i <= 99; ++ i) ten7[i]=ten7[i-1] * 10%7;
for(LL i = 1; i <= 19; ++ i) {
for(LL j = 0; j < 10; ++ j) {
LL sumlim = (i-1) * 9;
for(LL sum = 0; sum <= sumlim; ++ sum) {
for(LL modres = 0; modres < 7; ++ modres) {
for(LL has7 = 0; has7 < 2; ++ has7) {
LL nbit = i, nsum = sum + j, nmod = (modres+ten7[i-1]*j%7)%7,nhas
= has7 | (j == 7);
if( nsum <= sumlim + 9) {
update(f[nbit][nsum][nmod][nhas],
f[i-1][sum][modres][has7]);
}
}
}
}
}
}
// sum1
for(LL i = 1; i <= 19; ++ i) {
for(LL j = 0; j < 10; ++ j) {
LL sumlim = (i-1) * 9;
for(LL sum = 0; sum <= sumlim; ++ sum) {
for(LL modres = 0; modres < 7; ++ modres) {
for(LL has7 = 0; has7 < 2; ++ has7) {
LL nbit = i, nsum = sum + j, nmod = (modres+ten7[i-1]*j%7)%7,nhas
= has7 | (j == 7);
if( nsum <= sumlim + 9) {
update(sum1[nbit][nsum][nmod][nhas],
MOD( sum1[i-1][sum][modres][has7]+((ten[i-1]*j)%P)*f[i-1][sum][modres][has7]%P ) );
}
}
}
}
}
}
// sum2
for(LL i = 1; i <= 19; ++ i) {
for(LL j = 0; j < 10; ++ j) {
LL sumlim = (i-1) * 9;
for(LL sum = 0; sum <= sumlim; ++ sum) {
for(LL modres = 0; modres < 7; ++ modres) {
for(LL has7 = 0; has7 < 2; ++ has7) {
LL nbit = i, nsum = sum + j, nmod = (modres+ten7[i-1]*j%7)%7,nhas
= has7 | (j == 7);
if( nsum <= sumlim + 9) {
LL tmp = 0, cnt = f[i-1][sum][modres][has7];
tmp = MOD(tmp + sum2[i-1][sum][modres][has7]);
tmp = MOD(tmp + (ten[2*i-2]*j*j)%P*cnt%P);
tmp = MOD(tmp + (ten[i-1]*j*2)%P*sum1[i-1][sum][modres][has7]%P);
update(sum2[nbit][nsum][nmod][nhas],
tmp );
}
}
}
}
}
}
}
bool ok(LL x) {
LL dsum = 0;
if(x % 7 == 0) return true;
while(x) {
LL d = x % 10;
if(d == 7) return true;
dsum += d; x/=10;
}
return dsum %7 == 0;
}
LL mul(LL a, LL b, LL c) {
a %= c;
b %= c;
return a*b % c;
}
LL gao(LL x) {
if(x <= 0) return 0;
LL ans = ok(x)?0:mul(x,x,P);
//cout << x <<" ans = " << ans << endl;
LL has7 = 0;
LL sum = 0;
LL modres = 0;
LL pre = 0;
LL d[22],dlen=0;
while(x) d[dlen++]=x%10,x/=10;
for(LL i = dlen-1,j=0;i>=0;--i,++j) {
for(LL dg = 0; dg < d[i]; ++ dg) {
for(LL dsum = 0; dsum <= i*9; ++ dsum) {
for(LL md = 0; md < 7; ++ md) {
for(LL msk = 0; msk < 2; ++ msk) {
LL mdsum = dsum + dg + sum;
LL tmp = (modres*10 + dg)%7;
LL mmd = (ten7[i]*tmp+md)%7;
LL mmsk = has7 | msk | (dg == 7);
if( mmsk == 1 || mdsum % 7 ==0 || mmd == 0) continue;
LL dd = (pre * 10 + dg) % P;
LL cct = f[i][dsum][md][msk];
update(ans, sum2[i][dsum][md][msk]);
update(ans, MOD(((dd*2%P)*ten[i])%P*sum1[i][dsum][md][msk]%P));
update(ans, ((((dd*dd%P)*ten[i])%P*ten[i])%P*cct)%P);
}
}
}
}
if(d[i] == 7) has7 = 1;
sum += d[i];
modres = (modres*10+d[i])%7;
pre = pre * 10 + d[i];
}
return ans;
}
int main(){
pre();
LL T;
cin >> T;
while(T --) {
LL L, R; cin >> L >> R;
cout << MOD( gao(R)-gao(L-1) ) <<endl;
/*
LL ans = 0;
for(LL x = L; x <= R; ++ x) if(!ok(x))
update(ans, mul(x,x,P));
cout << ans << endl;*/
}
return 0;
}
2.SGU 258
problem
一个2*n位数,前n位数各位数和与后n位数各位数和相等,是lucky数。一个2*n位数,改变一个数字后,依然是2*n位(改前改后都没有前导零),并且是lucky数,则改之前的数称之为近似lucky数。求[l, r] 区间内,有多少近似lucky数。
think
dp[枚举到那一位][这个数是2*n位数][sum(前n位各位数和-后n位数各位数和)][more(最多增加)][less(最多减少)]sum可能<0 所以给他都+45more = max(9-前n位某个数, 后n位某个数)less = max(首位-1, 前n位且非首位的某个数, 9-后n位某个数) //因为首位不能变成0 所以首位-1答案是sum!=45(是0 的话就不用变了) && sum+more >=45 && sum-less<=45
code
const int M = 45;
int f[10][10][91][10][10];
int b[11];
int dfs(int pos, int N, int sum, int more, int less, int e){
if(pos==0){
return sum!=M && sum+more>=M && sum-less<=M;
}
if(!e && ~f[pos][N][sum][more][less]) return f[pos][N][sum][more][less];
int ans = 0;
int u = e?b[pos]:9;
int d = pos==N?1:0;
for(; d<=u; d++){
int ss = pos>N/2 ? sum+d : sum-d;
int mm = pos>N/2 ? max(more, 9-d) : max(more, d);
int ll = pos>N/2 ? max(less, pos==N?d-1:d) : max(less, 9-d);
ans += dfs(pos-1, N, ss, mm, ll, e&&d==u);
}
return e ? ans : f[pos][N][sum][more][less] = ans;
}
int s(int n){
if(n==-1) return 0;
int p = 0;
while(n){
b[++p] = n%10;
n/=10;
}
int ans = 0;
for(int i=2; i<=p; i+=2){
ans += dfs(i, i, 0+M, 0, 0, i==p);
}
return ans;
}
int main(){
memset(f, -1, sizeof(f));
int a, b;
while(~scanf("%d%d", &a, &b)){
printf("%d\n", s(b)-s(a-1));
}
return 0;
}
3.URAL 1057
problem
[l, r] 区间内,有多少个数分解成K个不同B的次方。
think
平时写数位DP,习惯把数位按十进制分解,这道题,分解成B进制,就可以了。然后看有多少个数里面有K个1 其他都是0.
code
int K, B;
int f[50][50];
int bit[50];
int dfs(int pos, int num, bool e){
if(pos==0) return num==K;
if(!e && ~f[pos][num]) return f[pos][num];
int ans = 0;
int u = e?bit[pos]:B-1;
for(int d=0; d<=1 && d<=u; d++){
ans += dfs(pos-1, num+d, e&&d==u);
}
return e?ans:f[pos][num]=ans;
}
int s(int n){
int p = 0;
while(n){
bit[++p] = n%B;
n/=B;
}
return dfs(p, 0, true);
}
int main(){
int n, m;
while(~scanf("%d%d%d%d", &n, &m, &K, &B)){
memset(f, -1, sizeof(f));
printf("%d\n", s(m)-s(n-1));
}
return 0;
}