【NOIP2017提高A组模拟8.22】密码

Description:

【NOIP2017提高A组模拟8.22】密码_第1张图片
1<=n<=1e1000, 1 <= p <= 1e9, 1 <= k <= 1e9

题解:

题目要求:
ni=1ij=1[Cjip>=k]
我们知道有库默尔定理,这是这道题的基础。
库默尔定理:
[Cji+p=i+j
所以此题可以转换为:
ni=1nj=1[i+j<=n][i+j]
n这么大,肯定数位dp了,但是好像很复杂。
先把n转成p进制。
fi,j,0/1,0/1 分别表示做了前i位(从低位到高位),一共进位了j次,当前第i位是否进位,以及组成的数是否<=n的前i位。
暴力转移:枚举当前位上的两个数。
随便分类讨论一波,发现贡献是等差数列,于是转移成O(1)的,细节超繁琐, 见代码。

#include 
#include 
#define ll long long
#define fo(i, x, y) for(ll i = x; i <= y; i ++)
#define fd(i, x, y) for(ll i = x; i >= y; i --)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;

const ll w = 4, M = 10000, mo = 1e9 + 7;
const ll ni = 5e8 + 4;

char s[10005];
struct node {
    ll w, a[10005];
}n;
ll len, num[10005];
ll a[10000], p, k;
ll f[2000][2000][2][2];

ll chu(node &a, ll b) {
    ll y = 0;
    fd(i, a.w, 1) {
        a.a[i] += y * M;
        y = a.a[i] % b; a.a[i] /= b;
    }
    while(a.w > 1 && a.a[a.w] == 0) a.w --;
    return y;
}

void Init() {
    scanf("%s", s + 1); len = strlen(s + 1);
    scanf("%lld %lld", &p, &k);
    n.w = 0; ll v = 0;
    fd(i, len, 1) {
        v = (v + 1) % 4;
        n.w += v == 1; num[i] = n.w;
    }
    fo(i, 1, len) n.a[num[i]] = n.a[num[i]] * 10 + s[i] - 48;
    while(!(n.w == 1 && n.a[1] == 0))
        a[++ a[0]] = chu(n, p);
}

ll s1(ll x, ll y) {
    if(x > y) return 0;
    x %= mo; y %= mo;
    ll z = (y - x + 1) % mo;
    return (2 * p - 1) % mo * z % mo - (x + y) * z % mo * ni % mo;
}

ll s2(ll x, ll y) {
    if(x > y) return 0;
    x %= mo; y %= mo;
    return (x + y + 2) % mo * (y - x + 1) % mo * ni % mo;
}

void End() {
    f[0][0][0][0] = 1;
    fo(i, 1, a[0]) {
        fo(j, 0, i) {
            fo(la, 0, 1) {
                if(a[i] >= la)
                    f[i][j][0][0] += f[i - 1][j][la][0] * s2(a[i] - la, a[i] - la) % mo,
                    f[i][j][0][1] += f[i - 1][j][la][1] * s2(a[i] - la, a[i] - la) % mo;
                f[i][j][0][0] += (f[i - 1][j][la][0] + f[i - 1][j][la][1]) % mo * s2(0, a[i] - la - 1) % mo;
                f[i][j][0][1] += (f[i - 1][j][la][0] + f[i - 1][j][la][1]) % mo * s2(a[i] - la + 1, p - 1 - la) % mo;
                f[i][j][0][0] %= mo; f[i][j][0][1] %= mo;
                if(j == 0) continue;

                f[i][j][1][0] += f[i - 1][j - 1][la][0] * s1(a[i] - la + p, a[i] - la + p) % mo;
                f[i][j][1][1] += f[i - 1][j - 1][la][1] * s1(a[i] - la + p, a[i] - la + p) % mo;
                f[i][j][1][0] += (f[i - 1][j - 1][la][0] + f[i - 1][j - 1][la][1]) % mo * s1(p - la, a[i] - la - 1 + p) % mo;
                f[i][j][1][1] += (f[i - 1][j - 1][la][0] + f[i - 1][j - 1][la][1]) % mo * s1(a[i] - la + 1 + p, 2 * p - 1) % mo;    
                f[i][j][1][0] %= mo; f[i][j][1][1] %= mo;           
            }
        }
    }
}

int main() {
    freopen("password.in", "r", stdin);
    freopen("password.out", "w", stdout);
    Init();
    if(k > a[0]) {
        printf("0\n");
        return 0;
    }
    End();
    ll ans = 0;
    fo(i, k, a[0]) ans += f[a[0]][i][0][0];
    ans  = (ans % mo + mo) % mo;
    printf("%lld", ans);
}

你可能感兴趣的:(数位dp,数论杂集)