bzoj1799 [Ahoi2009]self 同类分布

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1799

【题解】

一眼看过去,数位dp!

想了想,发现数字和最多也就是$m = 9 \times 18 = 162$种,好像不是很大。

考虑枚举每种数字和$p$,做一遍dp。

设$f_{i,j,k}$表示到第$i$位,当前真实数字模$p$余$j$,当前所有数字的和为$k$的方案数。(不考虑前导0问题)

这个可以通过一个$O(18\times p^2 \times 10)$的动态规划解决。

接下来按数位dp套路,改成前缀和,然后分情况:

位数小于它的,枚举最高位(由于不考虑前导零),计算。

位数等于它的,先枚举最高改变的位置,再枚举改变成了什么,就能推出后面$j$和$k$的两维,统计答案了。

最后可能要再统计下端点的,可能没统计进去(视具体写法而定)

复杂度主要是算dp数组,为$O(18^2 \times m^3)$,假装能过。

按照真实的复杂度实际上只要跑4亿多次,能过(挺正常)

实测能过,跑的贼慢。。卡了卡常从39s跑到了9s。

可能有一些更好的记忆化写法?姿势水平不够先不管qwq

被一个数组没开大错误卡了一晚上。。。win下测一点问题没有,linux就gg了。。

# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 19;
const int mod = 1e9+7;

ll A, B;
int w[22], wn;
ll f[22][210][210], bin[22]; 
// 到第i位,模p为j,位数和为k的方案数。 
 
inline ll solve(ll x) {
    if(x<=0) return 0; 
    ll tx = x; wn = 0;
    while(tx) w[++wn] = tx % 10, tx /= 10; 
    ll ret = 0;
    for (int p=1; p<=wn*9; ++p) {
        bin[0] = 1 % p; 
        for (int i=1; i<=wn; ++i) bin[i] = bin[i-1] * 10 % p;
        memset(f, 0, sizeof f);
        f[0][0][0] = 1;  
        for (int i=0; i<wn; ++i)
            for (int j=0; j<p; ++j) 
                for (int k=0; k<=p; ++k) 
                    for (int t=0; t<=9; ++t) 
                        if(k+t <= p) f[i+1][(j + t*bin[i]) % p][k+t] += f[i][j][k];   
        for (int i=wn-1; i; --i) 
            for (int t=1; t<=9; ++t) 
                if(p-t >= 0) ret += f[i-1][(p - t*bin[i-1]%p) % p][p-t];
        for (int i=wn, las=0, las2=0; i; --i) { 
            for (int t=(i==wn ? 1 : 0); t<w[i]; ++t) 
                if(p-t-las2 >= 0) ret += f[i-1][(p - (las + t*bin[i-1])%p) % p][p-t-las2]; 
            las += w[i]*bin[i-1];
            las %= p; las2 += w[i]; 
        }
    }
    int t = 0;
    for (int i=wn; i; --i) t += w[i];
    if(x%t == 0) ++ret; 
    return ret;
}

int main() {
    cin >> A >> B;
    cout << solve(B) - solve(A-1) << endl;
    return 0;
}
View Code

卡常版本(预处理一些东西)

# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 2e5 + 10;
const int mod = 1e9+7;

ll A, B;
int w[22], wn;
int bin[22], b[180][22]; 
ll f[22][180][180];
// 到第i位,模p为j,位数和为k的方案数。 
 
inline ll solve(ll x) {
    if(x<=0) return 0; 
    ll tx = x; wn = 0;
    while(tx) w[++wn] = tx % 10, tx /= 10; 
    ll ret = 0;
    for (int p=1; p<=wn*9; ++p) {
        int *bin = b[p];
        f[0][0][0] = 1;  
        for (int i=0; i<wn; ++i)
            for (int j=0; j<p; ++j) 
                for (int k=0; k<=p; ++k) 
                    for (int t=0; t<=9; ++t) 
                        f[i+1][(j + t*bin[i]) % p][k+t] += f[i][j][k];   
        for (int i=wn-1; i; --i) 
            for (int t=1; t<=9; ++t) 
                if(p-t >= 0) ret += f[i-1][(p - t*bin[i-1]%p) % p][p-t];
        for (int i=wn, las=0, las2=0; i; --i) { 
            for (int t=(i==wn ? 1 : 0); t<w[i]; ++t) 
                if(p-t-las2 >= 0) ret += f[i-1][(p - (las + t*bin[i-1])%p) % p][p-t-las2]; 
            las += w[i]*bin[i-1];
            las %= p; las2 += w[i]; 
        }
        for (int i=0; i<wn; ++i)
            for (int j=0; j<p; ++j)
                for (int k=0; k<=p; ++k) f[i][j][k] = 0;
    }
    int t = 0;
    for (int i=wn; i; --i) t += w[i];
    if(x%t == 0) ++ret; 
    return ret;
}

int main() {
    for (int p=1; p<=19*9; ++p) {
        b[p][0] = 1%p;
        for (int i=1; i<=19; ++i) b[p][i] = b[p][i-1]*10 % p;
    }
    cin >> A >> B;
    cout << solve(B) - solve(A-1) << endl;
    return 0;
}
View Code

 

posted @ 2017-07-03 22:17  Galaxies  阅读(224)  评论(0编辑  收藏  举报