abc194f O(nk)题解

前言

洛谷唯一的题解似乎是 \(O(nk^2)\) 的,怎么卡过去的orz

这里提供一种与 AT 官方题解时间复杂度相同的 \(O(nk)\) 做法。

Solution

题意很显然,就不解释了。

一眼丁真,考虑数位 dp。

\(dp_{i,j}\) 表示做到第 \(i\) 位,不同的个数有 \(j\) 种的方案数。

显然有转移方程:

\(dp_{i,j}=dp_{i-1,j}\times j +dp_{i-1,j-1} \times (16-j-1)\)

边界为 \(dp_{1,1}=15\)

分类讨论,考虑存在前导 0 与不存在前导 0。

当前导 0 存在时,由于每种方案中最高位数字的方案数是相等的,因此显然总方案数为 \(\frac{15}{16}\sum_{i=1}^{n}dp_{i,k}\)

而当不存在时,将数字分为已出现过和未出现过两种,分别计算个数。

两种唯一的区别为已钦定选择的数字个数。则在第 \(i\) 位时,若前面已有 \(x\) 个数已被选,方案数为 \(\binom{16-x}{k-x} \times \sum_{j=k-x}^{\min(n-i,k)} \frac{dp_{n-i,j}\times\binom{x}{j-k+x}}{\binom{16}{j}}\),具体可以用组合意义推导得到。

应该还可以再化简,但是这样就已经能做了。

实际实现的时候由于组合数的上下标不超过 16,因此可以提前预处理。

code

#include <bits/stdc++.h>
#define LL long long
using namespace std;
LL n,i,j,k,m,tmp1,tmp2,tmp3,ans=0;
LL mod=1000000007;
LL val[17],C[17][17],invC[17][17],dp[200005][17];
string s;
bool flag[17];
void exgcd(LL a,LL b,LL &x,LL &y,LL &val){
	if(b==0){
		val=a;
		x=1;
		y=0;
	}
	else{
		exgcd(b,a%b,y,x,val);
		y-=x*(a/b);
	}
}
LL doit(char ch){
	if(ch>='0' && ch<='9') return ch-'0';
	else return ch-'A'+10;
}
LL inv(LL x){
	tmp1=0,tmp2=0,tmp3=0;
	exgcd(x,mod,tmp1,tmp2,tmp3);
	return (tmp1+mod)%mod;
}
int main() {
    val[0]=1;
    for(i=1;i<=16;i++){
    	val[i]=val[i-1]*i%mod;
    	val[i]%=mod;
	}
	for(i=1;i<=16;i++)
	  for(j=0;j<=i;j++)
	    C[i][j]=val[i]*inv(val[i-j])%mod*inv(val[j])%mod;
	for(i=1;i<=16;i++)
	  for(j=0;j<=i;j++)
	    invC[i][j]=inv(C[i][j]);
	cin>>s>>k;
	n=s.length();
	dp[0][0]=1;
	dp[1][1]=16;
	C[0][0]=1;
	invC[0][0]=1;
	for(i=2;i<=n;i++)
	  for(j=1;j<=16;j++){
	  	dp[i][j]=(dp[i-1][j]*j%mod+dp[i-1][j-1]*(16-(j-1))%mod);
	  	dp[i][j]%=mod;
	  }
	for(i=1;i<n;i++){
		ans+=(dp[i][k]*inv(16)%mod*15ll%mod)%mod;
		ans%=mod;
	}
	ans+=((dp[n-1][k-1])*1ll*C[15][k-1]%mod*invC[16][k-1]%mod+(dp[n-1][k]*1ll*C[15][k-1]%mod*invC[16][k])%mod)%mod*(doit(s[0])-1)%mod;
	ans%=mod;
	LL sum1,sum2,now=1;
	flag[doit(s[0])]=true;
	for(i=1;i<s.length();i++){
		sum1=0,sum2=0;
	    for(j=0;j<doit(s[i]);j++){
	    	if(flag[j]==true){
	      	    sum1++;
		    }
		    else sum2++;
		}
		for(j=k-now;j<=k;j++){
			ans+=sum1*dp[n-i-1][j]%mod*C[16-now][k-now]%mod*C[now][j-(k-now)]%mod*invC[16][j]%mod;
			ans%=mod;
		}
		if(now+1<=k){
			for(j=k-now-1;j<=k;j++){
				ans+=sum2*dp[n-i-1][j]%mod*C[16-now-1][k-now-1]%mod*C[now+1][j-(k-now-1)]%mod*invC[16][j]%mod;
				ans%=mod;
			}
		}
		if(flag[doit(s[i])]==false) now++;
	    flag[doit(s[i])]=true;
	    if(now>k) break;
	}
	if(now==k) ans++;
	printf("%lld",ans%mod);
	  
	return 0;
}

posted @ 2023-11-02 13:48  monster_hunterqwq  阅读(9)  评论(0编辑  收藏  举报