HDU - 6056 simple counting problem 题解

HDU - 6056 simple counting problem 题解

——【UNR #2】梦中的题面 原版 & 加强版题目


知识点

组合数学,容斥,斯特林数,计数 DP。

分析

首先第一步容斥,考虑钦定某些一定 \(i\) 超过 \(b_i-c\) 的限制,即至少选 \(b_i-c+1\) 个,然后其余不被钦定的就随意。那么对于一个集合被钦定的集合 \(S\),运用插板法,答案就是:

\[{(n-1) - \sum_{i\in S}(b^i-c+1)+m \choose m} \\ \]

那么我们考虑容斥所有集合方案数,则得到:

\[\sum_S (-1)^{|S|}{(n-1) - \sum_{i\in S}(b^i-c+1)+m \choose m} \\ \]

考虑把 \(\sum_{i\in S} b_i\) 单独提取出来,则得到:

\[\sum_S (-1)^{|S|}{[n+m+(c-1)|S|-1] - \sum_{i\in S}b^i\choose m} \\ \]

那么可以考虑枚举 \(|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} \\ \]

还有上升幂与普通幂的相互转化等,实质与二项式反演有关。

那么就有了公式:

\[{n\choose m} = \frac{\sum_{i=0}^m (-1)^{m-i}\left[m \atop i\right]n^i}{m!}\\ \]

这样我们就可以把它变成 \(k\) 次多项式,令 \(val=n+m+(c-1)|S|-1\),原式为:

\[\sum_{S}(-1)^{|S|}\frac{\sum_{i=0}^m (-1)^{m-i}\left[m \atop i\right](val-\sum_{j\in S}b^j)^i}{m!}\\ \]

那么我们现在要算的就是 \((\sum_{j\in S}b^j)^i\),这个我们可以 DP 计算,设 \(f_{i,j,k}\) 表示在前 \(i\)\(b^1,b^2,\ldots,b^i\) 中选了 \(j\) 个数,它们的和的 \(k\) 次方之和。由二项式定理得到转移式:

\[f_{0,0,0} = 1\\ f_{i,j,k} = f_{i-1,j,k} + \sum_{l=0}^k {k\choose l} b^{i(k-l)}f_{i-1,j-1,l} \\ \]

那么枚举 \(|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;
}

参考 & 鸣谢

HDU 6056 simple counting problem_hdu6056-CSDN博客

posted @ 2025-08-17 22:09  Add_Catalyst  阅读(13)  评论(0)    收藏  举报