P5824 十二重计数法
十二重计数法 ×
刻刻帝 十二种子弹 √
以下复杂度均在已经预处理完逆元、阶乘逆元和阶乘的情况下计算。
咋一股 这玩意的味道。。。不过简单多了
- 球之间互不相同,盒子之间互不相同。
显然是 \(m^n\)。复杂度 \(O(\log n)\)。
ll Aleph(){return qpow(m,n);}
- 球之间互不相同,盒子之间互不相同,每个盒子至多装一个球。
一个一个放,所以是 \(m^\underline n\)。复杂度 \(O(n)\)。
ll Bet(){
ll ret=1;
rep(i,m-n+1,m)ret=ret*i%P;
return ret;
}
- 球之间互不相同,盒子之间互不相同,每个盒子至少装一个球。
对 1 考虑做容斥,所以答案是
复杂度 \(O(m\log n)\)。
另外你也可以考虑用斯特林数,答案就是 \(\begin{Bmatrix}n\\m\end{Bmatrix}\times m!\),这里采用的是前面的做法。
ll Gimel(){
ll ret=0;
rep(i,0,m)
if(i&1)ret=(ret-C(m,i)*qpow(m-i,n)%P+P)%P;
else ret=(ret+C(m,i)*qpow(m-i,n)%P)%P;
return ret;
}
- 球之间互不相同,盒子全部相同。
枚举非空盒子个数,所以是
来一发 第二类斯特林数·行,求个前缀和即可。复杂度 \(O(n\log n)-O(1)\)(固定 \(n\))。
void Zaphkiel(){
vec f(n+1),g(n+1);
rep(i,0,n){
f[i]=1ll*qpow(i,n)*invfac[i]%P;
g[i]=invfac[i];
if(i&1)g[i]=P-g[i];
}
S=mul(f,g,n<<1);
S.resize(n+1);
}
ll Dalet(){
ll ret=0;
rep(i,1,min(m,n))ret=(ret+S[i])%P;
return ret;
}
同时第三题的代码也可以简化为:
ll Gimel(){return 1ll*(m>n?0:S[m])*fac[m]%P;}
- 球之间互不相同,盒子全部相同,每个盒子至多装一个球。
显然是 \([n\le m]\)。复杂度 \(O(1)\)。
ll He(){return (n<=m);}
- 球之间互不相同,盒子全部相同,每个盒子至少装一个球。
显然是 \(\begin{Bmatrix}n\\m\end{Bmatrix}\),可以使用通项(不过也可以用 4 中算的整),复杂度 \(O(n\log m)/O(n\log n)-O(1)\)(固定 \(n\))。
ll Vav(){return S[m];}
- 球全部相同,盒子之间互不相同。
小学奥数插板法可知为 \(\dbinom{n+m-1}{m-1}\),复杂度 \(O(1)\)。
ll Zayin(){return C(n+m-1,m-1);}
- 球全部相同,盒子之间互不相同,每个盒子至多装一个球。
显然是 \(\dbinom{m}{n}\),复杂度 \(O(1)\)。
ll Het(){return C(m,n);}
- 球全部相同,盒子之间互不相同,每个盒子至少装一个球。
小学奥数插板法可知为 \(\dbinom{n-1}{m-1}\),复杂度 \(O(1)\)。
ll Tet(){return C(n-1,m-1);}
- 球全部相同,盒子全部相同。
本题唯一有点意思的东西。所以另外十一题放这干啥,搁这十二合一恶心人呢
考虑一个经典的 dp:\(f_{i,j}\) 表示 \(i\) 个球,\(j\) 个盒子。那么容易得到
那么考虑设 \(F_j(x)\) 为 \(\{f_{i,j}\}_{i=0}^{\infty}\) 的 OGF,可得:
暴力计算出 \(\ln F_m(x)\) 对其求 exp 即可,复杂度 \(O(n\log n)-O(1)\)(固定 \(m\))。
void Zaphkiel(){
...
rep(i,0,n)f[i]=0;
rep(i,1,m)for(ll j=1;i*j<=n;j++)f[i*j]=(f[i*j]+inv[j])%P;
getexp(f,F,n+1);
}
ll Yod(){return F[n];}
- 球全部相同,盒子全部相同,每个盒子至多装一个球。
显然是 \([n\le m]\)。复杂度 \(O(1)\)。
ll Yod_Alef(){return (n<=m);}
- 球全部相同,盒子全部相同,每个盒子至少装一个球。
每个盒子扔掉一个球就变成了 10。复杂度 \(O(n\log n)-O(1)\)(固定 \(m\))。
ll Yod_Bet(){return n<m?0:F[n-m];}
最后缝合一下就是完整代码了,如下:
// Problem: P5824 十二重计数法
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5824
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define endl '\n'
#define rep(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
using namespace std;
typedef long long ll;
inline void chkmax(ll &x,ll y){if(x<y)x=y;}
inline void chkmin(ll &x,ll y){if(x>y)x=y;}
struct IO_Tp {
const static int _I_Buffer_Size = 2 << 22;
char _I_Buffer[_I_Buffer_Size], *_I_pos = _I_Buffer;
const static int _O_Buffer_Size = 2 << 22;
char _O_Buffer[_O_Buffer_Size], *_O_pos = _O_Buffer;
IO_Tp() { fread(_I_Buffer, 1, _I_Buffer_Size, stdin); }
~IO_Tp() { fwrite(_O_Buffer, 1, _O_pos - _O_Buffer, stdout); }
IO_Tp &operator>>(ll &res) {
ll f=1;
while (!isdigit(*_I_pos)&&(*_I_pos)!='-') ++_I_pos;
if(*_I_pos=='-')f=-1,++_I_pos;
res = *_I_pos++ - '0';
while (isdigit(*_I_pos)) res = res * 10 + (*_I_pos++ - '0');
res*=f;
return *this;
}
IO_Tp &operator<<(ll n) {
if(n<0)*_O_pos++='-',n=-n;
static char _buf[10];
char *_pos = _buf;
do
*_pos++ = '0' + n % 10;
while (n /= 10);
while (_pos != _buf) *_O_pos++ = *--_pos;
return *this;
}
IO_Tp &operator<<(char ch) {
*_O_pos++ = ch;
return *this;
}
} IO;
ll n,m;
typedef vector<int> vec;
const int N=4194304,P=998244353;
int inv[N],fac[N],invfac[N],pw[N];
namespace Poly{
const int G=3,img=86583718;
int lmt,rev[N];
inline int qpow(int a,int k){
int ret=1;
while(k){
if(k&1)ret=1LL*ret*a%P;
a=1LL*a*a%P;
k>>=1;
}
return ret%P;
}
inline void init(int n){
lmt=1;int t=0;
while(lmt<n)lmt<<=1,t++;
for(int i=1;i<lmt;i++)rev[i]=(rev[i>>1]>>1)|(i&1)<<(t-1);
}
inline void NTT(vec&A,int lmt,int tp){
if(A.size()<lmt)A.resize(lmt);
for(int i=0;i<lmt;i++)if(i<rev[i])swap(A[i],A[rev[i]]);
for(int m=1,t=0;m<lmt;m<<=1,t++)
for(int j=0,Wn=pw[t+1];j<lmt;j+=m<<1)
for(int k=0,w=1,x,y;k<m;k++,w=1LL*w*Wn%P)
x=A[j+k],y=1LL*w*A[j+k+m]%P,A[j+k]=(x+y)%P,A[j+k+m]=(x-y+P)%P;
if(tp==1)return;
reverse(A.begin()+1,A.begin()+lmt);
for(int i=0,inv=qpow(lmt,P-2);i<lmt;i++)A[i]=1LL*A[i]*inv%P;
}
vec mul(vec f,vec g,int len){
init(len);
f.resize(lmt),g.resize(lmt);
NTT(f,lmt,1);NTT(g,lmt,1);
for(int i=0;i<lmt;i++)f[i]=1LL*f[i]*g[i]%P;
NTT(f,lmt,-1);
return f;
}
void getinv(vec&f,vec&g,int len){
if(f.size()<(len<<2))f.resize(len<<2);
if(g.size()<(len<<2))g.resize(len<<2);
if(len==1){g[0]=qpow(f[0],P-2);return;}
getinv(f,g,len+1>>1);
init(len<<1);
vec c(lmt);
copy(f.begin(),f.begin()+len,c.begin());
NTT(c,lmt,1),NTT(g,lmt,1);
for(int i=0;i<lmt;i++)g[i]=1LL*(2LL-1LL*g[i]*c[i]%P+P)%P*g[i]%P;
NTT(g,lmt,-1);
for(int i=len;i<lmt;i++)g[i]=0;
}
vec getdev(vec f,int len){
vec g(len-1);
for(int i=1;i<len;i++)g[i-1]=1LL*i*f[i]%P;
return g;
}
vec getinvdev(vec f,int len){
vec g(len+1);
for(int i=1;i<len;i++)g[i]=1LL*f[i-1]*inv[i]%P;
return g;
}
vec getln(vec f,int len){
vec a=getdev(f,len),b;
getinv(f,b,len);
return getinvdev(mul(a,b,len<<1),len);
}
void getexp(vec f,vec&g,int len){
if(g.size()<(len<<2))g.resize(len<<2);
if(len==1){g[0]=1;return;}
getexp(f,g,len+1>>1);
init(len<<1);
vec d=getln(g,len),e(f.begin(),f.begin()+len);
NTT(g,lmt,1),NTT(d,lmt,1),NTT(e,lmt,1);
for(int i=0;i<lmt;i++)g[i]=1LL*(1-d[i]+e[i]+P)*g[i]%P;
NTT(g,lmt,-1);
for(int i=len;i<lmt;i++)g[i]=0;
}
vec getpow(vec f,int len,int k){
vec g(len),h=getln(f,len);
for(int i=0;i<len;i++)h[i]=1LL*h[i]*k%P;
getexp(h,g,len);
return g;
}
}
using namespace Poly;
vec S,F;
int Kurumi(int n){
int lmt=1;
inv[0]=inv[1]=1;
while(lmt<n)lmt<<=1;
for(int i=2;i<lmt;i++)inv[i]=1LL*(P-P/i)*inv[P%i]%P;
fac[0]=invfac[0]=1;
for(int i=1;i<lmt;i++)fac[i]=1LL*fac[i-1]*i%P,invfac[i]=1LL*invfac[i-1]*inv[i]%P;
pw[23]=Poly::qpow(Poly::G,119);
for(int i=22;i>=0;i--)pw[i]=1LL*pw[i+1]*pw[i+1]%P;
return lmt;
}
ll C(ll n,ll m){return 1ll*fac[n]*invfac[m]%P*invfac[n-m]%P;}
void Zaphkiel(){
vec f(n+1),g(n+1);
rep(i,0,n){
f[i]=1ll*qpow(i,n)*invfac[i]%P;
g[i]=invfac[i];
if(i&1)g[i]=P-g[i];
}
S=mul(f,g,n<<1);
S.resize(n+1);
rep(i,0,n)f[i]=0;
rep(i,1,m)for(ll j=1;i*j<=n;j++)f[i*j]=(f[i*j]+inv[j])%P;
getexp(f,F,n+1);
}
ll Aleph(){return qpow(m,n);}
ll Bet(){
ll ret=1;
rep(i,m-n+1,m)ret=ret*i%P;
return ret;
}
ll Gimel(){return 1ll*(m>n?0:S[m])*fac[m]%P;}
ll Dalet(){
ll ret=0;
rep(i,1,min(m,n))ret=(ret+S[i])%P;
return ret;
}
ll He(){return (n<=m);}
ll Vav(){return m>n?0:S[m];}
ll Zayin(){return C(n+m-1,m-1);}
ll Het(){return C(m,n);}
ll Tet(){return C(n-1,m-1);}
ll Yod(){return F[n];}
ll Yod_Alef(){return (n<=m);}
ll Yod_Bet(){return n<m?0:F[n-m];}
int main(){
IO>>n>>m;
Kurumi(n<<2);
Zaphkiel();
IO<<Aleph()<<endl;
IO<<Bet()<<endl;
IO<<Gimel()<<endl;
IO<<Dalet()<<endl;
IO<<He()<<endl;
IO<<Vav()<<endl;
IO<<Zayin()<<endl;
IO<<Het()<<endl;
IO<<Tet()<<endl;
IO<<Yod()<<endl;
IO<<Yod_Alef()<<endl;
IO<<Yod_Bet()<<endl;
return 0;
}
最后,简单讲一下这题和 more transformations of integer sequences 的关系(不知道的人可以看下 rqy 课件,或者这里也可以):
- \(\operatorname{AIJ}_m(a)\),其中 \(a_x=1,\forall x\)。
- \(\operatorname{AIJ}_m(a)\),其中 \(a_1=1,a_x=0,x>1\)。
- \(\operatorname{AIJ}_m(a)\),其中 \(a_1=0,a_x=1,x>1\)。
- \(\operatorname{AIK}_m(a)\),其中 \(a_x=1,\forall x\)。
- \(\operatorname{AIK}_m(a)\),其中 \(a_1=1,a_x=0,x>1\)。
- \(\operatorname{AIK}_m(a)\),其中 \(a_x=0,a_x=1,x>1\)。
- \(\operatorname{EIJ}_m(a)\),其中 \(a_x=1,\forall x\)。
- \(\operatorname{EIJ}_m(a)\),其中 \(a_1=1,a_x=0,x>1\)。
- \(\operatorname{EIJ}_m(a)\),其中 \(a_x=0,a_x=1,x>1\)。
- \(\operatorname{EIK}_m(a)\),其中 \(a_x=1,\forall x\)。
- \(\operatorname{EIK}_m(a)\),其中 \(a_1=1,a_x=0,x>1\)。
- \(\operatorname{EIK}_m(a)\),其中 \(a_x=0,a_x=1,x>1\)。
如以上有问题请指出,谢谢。