传送门
题解:
花了整整三个小时。。。
一开始往期望的线性性考虑,花了半天时间拆式子发现是个套娃整个人都不好了。
然后突然发现了这个东西的组合意义。
考虑我们每个点 i i i 向 [ 0 , i ) [0,i) [0,i) 随机连出 k k k 条边,允许重边且每条边互不相同,从点 0 0 0 跑出 k k k 个不同的小球,全部走到 n n n 的方案数,注意每条边都允许多个球通过。
这道题问的期望就是所有连边方案下上面的方案数之和除以方案总数。
于是一个比较显然的思路是记录哪些球停在哪个点来DP。
然后你会发现我们在转移系数上其实只关心数量而不关心实际是哪些,于是可以爆搜正整数拆分然后预处理转移。
但是一对一转移状态还是太多了,考虑下一个优化。
设 f [ i ] [ s t ] f[i][st] f[i][st] 表示所有球目前在前 i i i 个点上,且除了在 i i i 点以上的所有球按照下一条进行转移的边划分后形成正整数拆分为 s t st st 的方案数。
设 g [ i ] [ s t ] g[i][st] g[i][st] 表示所有球在前 i i i 个点上且按照下一条进行转移的边进行划分后正整数拆分为 s t st st 的方案数。
注意这里强行要求在拆分中没有划分到一起的点不得使用同一条边进行转移,且这里进行边划分之后不要求立即进行转移。
这样转移很好写而且转移数量也比一对一转移少了很多,可以AC,转移方式就是考虑还原标号的方案数和分配方案数即可,具体实现可以看代码。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second
cs int mod=998244353;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline int po(int a,int b){int r=1;for(;b;b>>=1,Mul(a,a))if(b&1)Mul(r,a);return r;}
inline void ex_gcd(int a,int b,int &x,int &y){
if(!b){x=1,y=0;return;}ex_gcd(b,a%b,y,x);y-=a/b*x;
}inline int inv(int a){int x,y;ex_gcd(mod,a,y,x);return x+(x>>31&mod);}
int fac[15];
inline int A(int n,int m){return fac[n]/fac[n-m];}
inline int C(int n,int m){return fac[n]/fac[m]/fac[n-m];}
cs int N=1e5+7;
cs int F=153,G=57;
int n,K,fct,gct,f[F],g[G];
struct atom{int id,coe,k;};
std::vector<atom> tf[F],tg[G];
std::map<std::vector<int>,int> fid,gid;
std::vector<std::vector<int> > p[15];
void dfs(int n,int rest,int las){
static std::vector<int> vec;
if(!rest)return p[n].push_back(vec);
for(int i=las;i<=rest;++i){
vec.push_back(i);
dfs(n,rest-i,i);
vec.pop_back();
}
}
void trans(int x,cs std::vector<int> &u,cs std::vector<int> &v){
int ct=1,coe=1,y=K-x;
static int Cu[11],Cv[11];
memset(Cu,0,sizeof Cu);
memset(Cv,0,sizeof Cv);
std::vector<int> p;
p.resize(u.size()+v.size());
std::merge(u.begin(),u.end(),
v.begin(),v.end(),p.begin());
for(int t:v){
coe*=C(y,t);
y-=t;++Cv[t];
}for(int t:u)++Cu[t];
for(int re i=1;i<=K;++i){
ct*=C(Cu[i]+Cv[i],Cu[i]);
coe/=fac[Cv[i]];
}int fi=fid[u],gi=gid[p];
tf[fi].push_back({gi,coe,0});
tg[gi].push_back({fi,mul(A(K,v.size()),ct),(int)v.size()});
}
void init(){
for(int re i=fac[0]=1;i<=10;++i)
fac[i]=fac[i-1]*i;
for(int re i=0;i<=K;++i){
dfs(i,i,1);
for(cs auto &v:p[i])
fid[v]=++fct;
}for(cs auto &v:p[K])
gid[v]=++gct;
for(int re i=0;i<=K;++i)
for(cs auto &u:p[i])
for(cs auto &v:p[K-i])
trans(i,u,v);
}
int pw[15];
void Main(){
scanf("%d%d",&n,&K);
init(),f[1]=pw[0]=1;
for(int re i=1;i<=n;++i){
memset(g+1,0,gct<<2);
for(int re j=1;j<=fct;++j)
if(f[j])for(auto &t:tf[j])
Inc(g[t.id],mul(f[j],t.coe));
for(int re j=1;j<=K;++j)
pw[j]=mul(pw[j-1],i);
memset(f+1,0,fct<<2);
for(int re j=1;j<=gct;++j)
if(g[j])for(auto &t:tg[j])
Inc(f[t.id],mul(g[j],mul(pw[K-t.k],t.coe)));
}int tot=1;
for(int re i=1;i<=n;++i)Mul(tot,i);
cout<<mul(f[1],inv(po(tot,K)))<<"\n";
}
inline void file(){
#ifdef zxyoi
freopen("nightmare.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
freopen("nightmare.in","r",stdin);
freopen("nightmare.out","w",stdout);
#endif
#endif
}signed main(){file();Main();return 0;}