HDU - 6056 simple counting problem 题解
HDU - 6056 simple counting problem 题解
——【UNR #2】梦中的题面 原版 & 加强版题目
知识点
组合数学,容斥,斯特林数,计数 DP。
分析
首先第一步容斥,考虑钦定某些一定 \(i\) 超过 \(b_i-c\) 的限制,即至少选 \(b_i-c+1\) 个,然后其余不被钦定的就随意。那么对于一个集合被钦定的集合 \(S\),运用插板法,答案就是:
那么我们考虑容斥所有集合方案数,则得到:
考虑把 \(\sum_{i\in S} b_i\) 单独提取出来,则得到:
那么可以考虑枚举 \(|S|\) 来求解。
对于一个 \(n\) 较大而 \(m\) 较小的组合数 \({n\choose m}\),我们有一种 \(O(m)\) 求解的方法,可以用斯特林数将下降幂转为普通幂:
下降幂与普通幂的相互转化
记 \(\left[n \atop k\right],\left\{n \atop k \right\}\) 分别表示第一类和第二类斯特林数,\(x^{\underline{k}}\) 表示下降阶乘幂 \(\frac{x!}{(x-k)!}\)。那么有:
\[x^n = \sum_k\left\{n \atop k \right\} x^{\underline{k}} \\ x^{\underline{n}} = \sum_k\left[n \atop k \right] (-1)^{n-k} x^{k} \\ \]还有上升幂与普通幂的相互转化等,实质与二项式反演有关。
那么就有了公式:
这样我们就可以把它变成 \(k\) 次多项式,令 \(val=n+m+(c-1)|S|-1\),原式为:
那么我们现在要算的就是 \((\sum_{j\in S}b^j)^i\),这个我们可以 DP 计算,设 \(f_{i,j,k}\) 表示在前 \(i\) 个 \(b^1,b^2,\ldots,b^i\) 中选了 \(j\) 个数,它们的和的 \(k\) 次方之和。由二项式定理得到转移式:
那么枚举 \(|S|\),再内层枚举 \(n+m+(c-1)|S|-1\) 转成 \(b\) 进制后脱离限制的最高位,就像数位 DP 一样。
最后相当于两个多项式相乘,得到 \((val-\sum_{j\in S}b^j)^i\) 即可算出答案。
代码
#include<bits/stdc++.h>
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define CPY(a,b,c,d) memcpy(a,b,sizeof(c)*(d))
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
constexpr int N(50+10),M(N*9),B(2300+10);
namespace Modular {
#define Mod 998244353
int fac[N],ifac[N];
int C[N][N],S1[N][N];
//Modular...
int Pow(int a,int b=Mod-2) {
int res(1);
for(a%=Mod; b; b>>=1,tomul(a,a))if(b&1)tomul(res,a);
return res;
}
void Init(const int n=N-5) {
/*DE("Binom");*/
FOR(i,0,n) {
C[i][0]=1;
FOR(j,1,i)C[i][j]=add(C[i-1][j-1],C[i-1][j]);
}
/*DE("First Stirling Number");*/
S1[0][0]=1;
FOR(i,1,n)FOR(j,1,i)S1[i][j]=add(S1[i-1][j-1],mul(i-1,S1[i-1][j]));
/*DE("Fact");*/
FOR(i,fac[0]=1,n)fac[i]=mul(fac[i-1],i);
ifac[n]=Pow(fac[n]);
DOR(i,n,1)ifac[i-1]=mul(ifac[i],i);
}
} using namespace Modular;
int cas,n,m,b,c,val,ans;
int dig[M],m_dig[B];
int f[N][N][N];
string str;
int Cmain() {
/*DE("Init");*/
m=str.size()-1;
FOR(i,0,m)dig[i]=str[i]^'0';
reverse(dig,dig+m+1);
//transform
int tmp(0);
while(m>=0) {
ll rem(0);
DOR(i,m,0)rem=rem*10+dig[i],dig[i]=rem/b,rem%=b;
m_dig[tmp++]=rem;
while(m>=0&&!dig[m])--m;
}
m=tmp-1;
//calculate
val=0;
DOR(i,m,0)tomul(val,b),toadd(val,m_dig[i]);
toadd(val,n-1);
/*DE("DP");*/
RCL(f,0,f,1),f[0][0][0]=1;
for(int i(1),w(b); i<=n; ++i,tomul(w,b)) {
static int pw[N];
FOR(j,pw[0]=1,n)pw[j]=mul(pw[j-1],w);
CPY(f[i],f[i-1],f[i],1);
FOR(j,1,i)FOR(k,0,n)FOR(l,0,k)toadd(f[i][j][k],mul(C[k][l],pw[k-l],f[i-1][j-1][l]));
}
/*DE("enumerate |S|");*/
ans=0;
FOR(t,0,n) {
static int pw_v[N],pw_b[B],coef[N];
FOR(i,pw_v[0]=1,n)pw_v[i]=mul(pw_v[i-1],val);
FOR(i,pw_b[0]=1,m)pw_b[i]=mul(pw_b[i-1],b);
FOR(i,0,n)coef[i]=0;
FOR(i,0,n)FOR(j,i,n) {
int ways(mul(S1[n][j],C[j][i],pw_v[j-i],ifac[n]));
toadd(coef[i],(n^j^i^t)&1?Mod-ways:ways);
}
int sum(0),cnt(0);
//enumerate Highest position
DOR(i,m,0)if(m_dig[i]) {
int lim(min(n,!i?0:i-1));
static int pw_s[N];
FOR(j,pw_s[0]=1,n)pw_s[j]=mul(pw_s[j-1],sum);
FOR(j,0,n) {
int ways(0);
FOR(k,0,j)toadd(ways,mul(C[j][k],f[lim][t-cnt][k],pw_s[j-k]));
toadd(ans,mul(ways,coef[j]));
}
if(cnt<t&&i<=n&&i) {
toadd(sum,pw_b[i]),++cnt;
if(m_dig[i]>1) {
FOR(j,pw_s[0]=1,n)pw_s[j]=mul(pw_s[j-1],sum);
FOR(j,0,n) {
int ways(0);
FOR(k,0,j)toadd(ways,mul(C[j][k],f[lim][t-cnt][k],pw_s[j-k]));
toadd(ans,mul(ways,coef[j]));
}
break;
}
} else break;
}
//update
m_dig[0]+=c-1,toadd(val,c-1>0?c-1:Mod+c-1);
if(c>1) {
FOR(i,0,m-1)if(m_dig[i]>=b)m_dig[i]-=b,++m_dig[i+1];
if(m_dig[m]>=b)m_dig[m]-=b,m_dig[++m]=1;
} else {
FOR(i,0,m-1)if(m_dig[i]<0)m_dig[i]+=b,--m_dig[i+1];
while(m&&!m_dig[m])--m;
if(m_dig[m]<0)break;
}
}
cout<<"Case #"<<++cas<<": "<<ans<<'\n';
return 0;
}
signed main() {
Init();
while(cin>>n>>b>>c>>str)Cmain();
return 0;
}

浙公网安备 33010602011771号