zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:密码(数位DP+库默尔定理)

    题目描述

    为了揭穿$SERN$的阴谋,$Itaru$黑进了$SERN$的网络系统。
    然而,想要完全控制$SERN$,还需要知道管理员密码。$Itaru$从截获的信息中发现,$SERN$的管理员密码是两个整数$l,s,0leqslant sleqslant l$,并且一旦得知了管理员密码,就可以生成出$SERN$各个网路接口的密码:各个网络接口的密码均是若干个长为$l$的$0/1$串,且每个串中$1$的个数恰为$s$。不难发现,生成的密码串个数是一个组合数。
    $SERN$的网络系统是由$p^k$个网络接口构成的,$SERN$为了保证网络系统的稳定性,保证了$p$为质数,且所有生成的密码串个数能被$p^k$整除。为了网络通讯的方便,$SERN$的网络接口的密码不会太长,即可以保证$lleqslant N$。
    作为一名$Super Hacker$,$Itaru$已经想到了破解密码的绝妙方法,然而在这之前,他需要确认管理员密码的可能情况有多少。由于答案可能很大,答案对${10}^9+7$取模。


    输入格式

    仅包含一行三个整数$N,p,k$。


    输出格式

    仅包含一个整数,表示答案。


    样例

    样例输入:

    4 2 2

    样例输出:

    2


    数据范围与提示

    样例解释:

    有$2$种可能的情况,$l=4,s=1;l=4s=3$。

    数据范围:

    保证$1leqslant p,kleqslant {10}^9,1leqslant Nleqslant {10}^{1000}$,$p$为质数。
    各个测试点还满足如下约束:


    题解

    又是数学题,那就化式子。

    组合数与阶乘有关,我们可以先考虑阶乘。
    对于$n!$,满足$p^k|n!$的最大的$k$为:$$maxk=sum limits_{i=1}^{infty}left lfloor frac{n}{p^i} ight floor$$
    那么对于$C_n^m$,由于$C_n^m=frac{n!}{m!(n-m)!}$,满足$p^k|C_n^m$的最大的$k$为:$$maxk=sum limits_{i=1}^{infty}left lfloor frac{n}{p^i} ight floor-left lfloor frac{m}{p^i} ight floor-left lfloor frac{n-m}{p^i} ight floor left lfloor frac{n-m}{p^i} ight floor$$

    然后我们引入一个新的名词:库默尔定理。

    那么,什么是库默尔定理呢?

    设$m,n$为正整数,$p$为素数,则$C_{m+n}^m$含$p$的幂次等于$m+n$在$p$进制下的进位次数。

    下面给出证明:

    组合数$C_{n+m}^m$所含$p$的幂次数为:$$sum limits_{i=1}^{infty}left lfloor frac{m+n}{p^i} ight floor-sum limits_{i=1}^{infty}left lfloor frac{n}{p^i} ight floor-sum limits_{i=1}^{infty}left lfloor frac{m}{p^i} ight floor \ =sum limits_{i=1}^{infty}(left lfloor frac{m+n}{p^i} ight floor-left lfloor frac{n}{p^i} ight floor-left lfloor frac{m}{p^i} ight floor)$$

    这是因为组合数公式$C_{n+m}^n=frac{(n+m)!}{n!m!}$以及$n!$含有素数$p$的幂次公式$vp(n!)=sum limits_{i=1}^{infty} left lfloor dfrac{n}{p^i} ight floor$。

    对于某个$p^i$,$left lfloor frac{m}{p^i} ight floor$等于$m$在$p$进制表示下去掉后$i$位,在第$i+1$位上,$n+m$在这一位上进位的充要条件是$$left lfloor frac{n+m}{p^i} ight floor-left lfloor frac{n}{p^i} ight floor-left lfloor frac{m}{p^i} ight floor=1$$不进位则$$left lfloor frac{n+m}{p^i} ight floor-left lfloor frac{n}{p^i} ight floor-left lfloor frac{m}{p^i} ight floor=0$$因此$sum limits_{i=1}^{infty}(left lfloor frac{m+n}{p^i} ight floor-left lfloor frac{n}{p^i} ight floor-left lfloor frac{m}{p^i} ight floor)$就是$n+m$在$p$进制下的进位次数。

    那么题目所求便是两个和不超过$N$的正整数,它们在$p$进制下做加法近位了至少$k$的可能方案数。

    那么考虑数位$DP$,设$dp[i][j][0/1][0/1]$表示到了$p$进制下的第$i$位,进位次数为$j$,下一位是否进位,前$i$位是否与$N$在$p$进制下相同的方案。

    时间复杂度:$Theta(S^2)$设$S$为$N$在$p$进制下的位数。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    char ch[1001];
    long long N[1001],S[1001];
    long long p,k;
    long long dp[1001][1001][2][2];
    long long ans;
    int main()
    {
    	scanf("%s%lld%lld",ch+1,&p,&k);
    	N[0]=strlen(ch+1);
    	for(int i=1;i<=N[0];i++)N[i]=ch[i]-'0';
    	for(int i=1;(i<<1)<=N[0];i++)
    		N[i]^=N[N[0]-i+1]^=N[i]^=N[N[0]-i+1];
    	while(N[0])
    	{
    		for(int i=N[0];i;i--)
    		{
    			if(i>1)N[i-1]+=N[i]%p*10;
    			else S[++S[0]]=N[i]%p;
    			N[i]/=p;
    		}
    		while(N[0]&&!N[N[0]]){N[0]--;if(N[0]<0){puts("0");return 0;}}
    	}
    	dp[0][0][0][0]=1;
    	for(int i=1;i<=S[0];i++)
    		for(int j=0;j<=i;j++)
    		{
    			dp[i][j][0][0]=(((S[i]+1)*(S[i]+2)>>1)%1000000007*dp[i-1][j][0][0]%1000000007+(S[i]*(S[i]+1)>>1)%1000000007*(dp[i-1][j][0][1]+dp[i-1][j][1][0])%1000000007+(S[i]*(S[i]-1)>>1)%1000000007*dp[i-1][j][1][1]%1000000007)%1000000007;
    			dp[i][j+1][0][1]=((((p<<1)-S[i]-2)*(S[i]+1)>>1)%1000000007*dp[i-1][j][0][0]%1000000007+(((p<<1)-S[i])*(S[i]+1)>>1)%1000000007*dp[i-1][j][0][1]%1000000007+(((p<<1)-S[i]-1)*S[i]>>1)%1000000007*dp[i-1][j][1][0]%1000000007+(((p<<1)-S[i]+1)*S[i]>>1)%1000000007*dp[i-1][j][1][1]%1000000007)%1000000007;
    			dp[i][j][1][0]=(((S[i]+p+2)*(p-S[i]-1)>>1)%1000000007*dp[i-1][j][0][0]%1000000007+((p+S[i])*(p-S[i]-1)>>1)%1000000007*dp[i-1][j][0][1]%1000000007+((p+S[i]+1)*(p-S[i])>>1)%1000000007*dp[i-1][j][1][0]%1000000007+((p+S[i]-1)*(p-S[i])>>1)%1000000007*dp[i-1][j][1][1]%1000000007)%1000000007;
    			dp[i][j+1][1][1]=(((p-S[i]-2)*(p-S[i]-1)>>1)%1000000007*dp[i-1][j][0][0]%1000000007+((p-S[i])*(p-S[i]-1)>>1)%1000000007*(dp[i-1][j][0][1]+dp[i-1][j][1][0])%1000000007+((p-S[i]+1)*(p-S[i])>>1)%1000000007*dp[i-1][j][1][1]%1000000007)%1000000007;
    		}
    	for(int i=k;i<=S[0];i++)
    		ans=(ans+dp[S[0]][i][0][0])%1000000007;
    	printf("%lld",ans);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    通过命令修改wampserver的mysql密码
    MOS管(场效应管)导通条件
    JQUERY1.9学习笔记 之属性选择器(二) 包含选择器
    JQUERY1.9学习笔记 之属性选择器(一) 前缀选择器
    JQUERY1.9学习笔记 之可见性过滤器(二) 可见选择器
    JQUERY1.9学习笔记 之可见性过滤器(一) 隐藏选择器
    [forwarding]Android上dip、dp、px、sp等单位说明
    [forwarding]Android 常用控件讲解
    [forwarding] mysql数据库的备份、导入、导出命令,以及数据的导入、导出
    【Make My Eclipse】之 使用正确的插件
  • 原文地址:https://www.cnblogs.com/wzc521/p/11548364.html
Copyright © 2011-2022 走看看