bzoj 1902: Zju2116 Christopher lucas定理 && 数位DP

1902: Zju2116 Christopher

Time Limit: 1 Sec  Memory Limit: 64 MB
Submit: 172  Solved: 67
[Submit][Status][Discuss]

Description

给定n个元素,要从中间选择m个元素有多少种方案呢?答案很简单,就是C(n,m)。如果一个整数m(0≤m≤n),C(n,m)是某一个质数p的倍数,那么这个m就是讨厌的数字,现在给定了p和n,求有多少个讨厌的数字。

Input

第一行是一个正整数n,(1≤n≤10100)。输入的第二行是一个质数p(1≤p≤107)。

Output

只有一行,表示讨厌的数字的个数。

Sample Input

6
2

Sample Output

3

HINT

30%的数据里,n≤1000; 100%的数据里,n≤10^100

第一次写lucas定理,觉得非常“机智”

将n,m分别分解p进制,发现如果某一位n[i]<m[i],则余数为0

注意,这里p非常大,所以一般数位dp中for一遍0到p是不可取的,要特殊判断关键点,对于[0,p-1]中转移相同的一起处理。

import sys;
def deal(x,y):
    x=(x+y)%p;
sys.stdin=open("input.txt","r");
n=int(raw_input());
p=int(raw_input());
a=[];
while (n):
    a.append(n%p);
    n/=p;
for i in range(0,len(a)/2):
    a[i],a[len(a)-i-1] = a[len(a)-i-1],a[i];
dp = [[[0 for k in range(0,2)] for j in range(0,2)] for i in range(0,len(a)+2)];
dp[0][0][1]=a[0];
dp[0][0][0]=1;
for i in range(1,len(a)):
    for j in range(0,2):
        for k in range(0,2):
            #print i,j,k;
            if (dp[i-1][j][k]==0):continue;
            if (k==0):
                dp[i][j][True] += dp[i-1][j][k]*a[i];
                dp[i][j][False] += dp[i-1][j][k];
            else:
                dp[i][j][True] += dp[i-1][j][k]*(a[i]+1);
                dp[i][True][True] += dp[i-1][j][k]*(p-a[i]-1);
print (dp[len(a)-1][1][0]+dp[len(a)-1][1][1]);
原文地址:https://www.cnblogs.com/mhy12345/p/4462836.html