每种堆法(理解成名次序列,举例3,3,8,2和7,7,100,2都对应2,2,1,3这个名次序列)等概率出现;题目中“两种堆法不同当且仅当某个积木在两种堆法中处于不同的层中”可见这是个组合问题,于是设一个名次的生成函数F(x)=sum i=1->inf 1*x^i/(i!) 。因为F(x)的常数项为0,有F^inf(x)=0。

名次序列的方案数的生成函数:A(x) = sum i=0->inf F^i(x) = 1/(1-F(x))

名次序列的各种方案的名次个数之和的生成函数:B(x) = sum i=0->inf i*F^i(x) = F(x)/(1-F(x))^2 = A^2(x)-A(x) = A(x)*(A(x)-1)

预处理出A(x),B(x)的前MAX_N项系数,对于n,答案为B(x)[n]/A(x)[n]。

注意事项

关于从0开始求和

如果是从1开始的情形:

A(x)=sum i=1->inf F^i(x)

= F(x)*(1-F^inf(x))/(1-F(x))

= F(x)/(1-F(x))

= (1-(1-F(x)))/(1-F(x))

= 1/(1-F(x))-1

B(x)=sum i=1-> inf i*F^i(x)

= ...

= F(x)/(1-F(x))^2-1

抛开A(x)[0]和B(x)[0],似乎关系并没有什么影响。

关于EGF的卷积运算

某个生成函数F(x)=sum i=0->inf a[i]*x^i/(i!)

对于EGF意义下的卷积

F(x)*F(x)=sum i=0->inf (sum j=0->i C(i,j)*a[j]*a[i-j]) x^i/(i!)

= sum i=0->inf (sum j=0->i (i!)/(j!)/((i-j)!)*a[j]*a[i-j]) x^i/(i!)

= sum i=0->inf (sum j=0->i a[j]/(j!)*a[i-j]/((i-j)!)) x^i

与在OGF意义下的卷积

F(x)=sum i=0->inf b[i]*x^i ,b[i]=a[i]/(i!)

F(x)*F(x)=sum i=0->inf (sum j=0->i b[j]*b[i-j]) x^i

完全一致.

这告诉我们在使用EGF的具体计算时,可以将1/(i!)放到系数中去用一般多项式算法计算得到一个OGF结构,

所得到的函数的各系数再乘上i!就是正确的EGF结构下的标志序列了;显然,该性质也适用于EGF的连续卷积!

换句话说,EGF的标志序列尽管除以一个i!,然后与OGF无异地各种操作,最终结果再让标志序列乘上i!就好了。

代码实现中因为除法关系没有乘上i!。

参考实现

#include <bits/stdc++.h>
using namespace std; const int MAX_N=1e5+10;
const int P=998244353,G=3; int a[MAX_N<<2];
int b[MAX_N<<2];
int c[MAX_N<<2];
int f[MAX_N<<2];
int r[MAX_N<<2];
int frr[MAX_N]; int qpow(long long x,int y) {
long long c=1;
for(; y; y>>=1, x=x*x%P)
if(y&1) c=c*x%P;
return c;
}
void ntt(int *a,int lmt,int tp) {
for(int i=0; i<lmt; ++i) if(i<r[i]) swap(a[i],a[r[i]]);
for(int m=1; m<lmt; m<<=1) {
int wm=qpow(G,(P-1)/(m<<1));
if(tp==-1) wm=qpow(wm,P-2);
for(int i=0; i<lmt; i+=(m<<1)) {
long long w=1, tmp;
for(int j=0; j<m; ++j, w=w*wm%P) {
tmp=w*a[i+j+m]%P;
a[i+j+m]=(a[i+j]-tmp+P)%P;
a[i+j]=(a[i+j]+tmp)%P;
}
}
}
if(tp==-1) {
long long tmp=qpow(lmt,P-2);
for(int i=0; i<lmt; ++i) a[i]=tmp*a[i]%P;
}
}
void polyrev(int deg,int *a,int *b) {
if(deg==1) {
b[0]=qpow(a[0],P-2);
return;
}
polyrev((deg+1)>>1,a,b);
int lmt=1, l=0;
while(lmt<(deg<<1)) lmt<<=1, ++l;
for(int i=0; i<lmt; ++i) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int i=0; i<deg; ++i) c[i]=a[i];
fill(c+deg,c+lmt,0);
ntt(c,lmt,1), ntt(b,lmt,1);
for(int i=0; i<lmt; ++i) b[i]=(2LL-1LL*c[i]*b[i]%P+P)%P*b[i]%P;
ntt(b,lmt,-1);
fill(b+deg,b+lmt,0);
} int main() {
long long fac=1;
for(int i=1; i<MAX_N; ++i) fac=fac*i%P;
frr[MAX_N-1]=qpow(fac,P-2);
for(int i=MAX_N-1; i; --i) frr[i-1]=1LL*i*frr[i]%P;
for(int i=0; i<MAX_N; ++i) a[i]=P-frr[i];
a[0]=(a[0]+2)%P;
polyrev(MAX_N,a,b);
for(int i=0; i<MAX_N; ++i) a[i]=f[i]=b[i];
int lmt=1;
while(lmt<=MAX_N) lmt<<=1; lmt<<=1;
a[0]=(a[0]+P-1)%P;
ntt(f,lmt,1), ntt(a,lmt,1);
for(int i=0; i<lmt; ++i) f[i]=1LL*f[i]*a[i]%P;
ntt(f,lmt,-1); int T,n;
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
printf("%lld\n",1LL*f[n]*qpow(b[n],P-2)%P);
}
return 0;
}

感谢@Weng_Weiji的答疑,思路也是来自于他(她?)的题解;代码参考了@Owen_codeisking

也感谢来自其它大佬的恶意嘲讽

[P5162] WD与积木的更多相关文章

  1. 洛谷 P5162 WD与积木 解题报告

    P5162 WD与积木 题目背景 WD整日沉浸在积木中,无法自拔-- 题目描述 WD想买\(n\)块积木,商场中每块积木的高度都是\(1\),俯视图为正方形(边长不一定相同).由于一些特殊原因,商家会 ...

  2. 洛谷P5162 WD与积木 [DP,NTT]

    传送门 思路 真是非常套路的一道题-- 考虑\(DP\):设\(f_n\)为\(n\)个积木能搭出的方案数,\(g_n\)为所有方案的高度之和. 容易得到转移方程: \[ \begin{align*} ...

  3. 洛谷 P5162 WD与积木【多项式求逆】

    设f[i]为i个积木能堆出来的种类,g[i]为i个积木能堆出来的种类和 \[ f[n]=\sum_{i=1}^{n}C_{n}^{i}g[n-i] \] \[ g[n]=\sum_{i=1}^{n}C ...

  4. P5162 WD与积木(多项式求逆+生成函数)

    传送门 题解 比赛的时候光顾着算某一个\(n\)的答案是多少忘了考虑不同的\(n\)之间的联系了--而且我也很想知道为什么推着推着会变成一个二项式反演-- 设\(f_n\)为\(n\)块积木时的总的层 ...

  5. Luogu5162 WD与积木(生成函数+多项式求逆)

    显然的做法是求出斯特林数,但没有什么优化空间. 考虑一种暴力dp,即设f[i]为i块积木的所有方案层数之和,g[i]为i块积木的方案数.转移时枚举第一层是哪些积木,于是有f[i]=g[i]+ΣC(i, ...

  6. [Luogu5162]WD与积木(多项式求逆)

    不要以为用上Stirling数就一定离正解更近,FFT都是从DP式本身出发的. 设f[i]为i个积木的所有方案的层数总和,g[i]为i个积木的方案数,则答案为$\frac{f[i]}{g[i]}$ 转 ...

  7. [luogu5162]WD与积木

    设$g_{n}$表示$n$个积木放的方案数,枚举最后一层所放的积木,则有$g_{n}=\sum_{i=1}^{n}c(n,i)g_{n-i}$(因为积木有编号的所以要选出$i$个) 将组合数展开并化简 ...

  8. 【LUOGU???】WD与积木 NTT

    题目大意 把 \(n\) 个有标号物品分到一些有标号的箱子中且不允许为空,问期望箱子的数量. 多组询问. \(n\leq 100000\) 题解 记 \(f_i\) 为 \(i\) 个有标号物品分到一 ...

  9. 【LGP5162】WD与积木

    题目 场面过度玄学,容易引起不适 我们发现我们要求的这个期望由分母和分子两部分构成 易发现 \[Ans=\frac{\sum_{i=1}^nS_2(n,i)\times i\times i!}{\su ...

随机推荐

  1. SDK更新

    虽然国内google被墙了,但仍可利用国内的某些镜像网站实现Android SDK在线更新,使用方法如下: 1.启动 Android SDK Manager ,打开主界面,依次选择『Tools』.『O ...

  2. [gazebo-1] process has died [pid 22855, exit code 255,

    [gazebo-1] process has died [pid 22855, exit code 255, cmd /opt/ros/kinetic/lib/gazebo_ros/gzserver ...

  3. echarts使用踩坑实录之气泡图

    最近想做一个统计文章点击率,评论率和点赞率的功能,听说echarts可以轻易完成它,于是我就选择使用echarts,考虑到我做的模块上文章是没有分类的,所以我的统计是基于一个个点,这一看嘛,感觉散点图 ...

  4. 更适用于JavaScript的设计模式:面向委托的设计,了解一下?(下)

    先来看一下传统的面向类式的写法: function Foo(name) { this.name = name; } Foo.prototype.sayName = function() { conso ...

  5. Educational Codeforces Round 58 (Rated for Div. 2) D 树形dp + 数学

    https://codeforces.com/contest/1101/problem/D 题意 一颗n个点的树,找出一条gcd>1的最长链,输出长度 题解 容易想到从自底向长转移 因为只需要g ...

  6. GitHub上高质量项目

    scribejava/scribejava:一个简单的 Java 实现的 OAuth/OAuth2 库winterbe/java8-tutorial:绝对值得一看的Java8指南.教程javaee-s ...

  7. bootstrap table使用参考

    https://www.cnblogs.com/landeanfen/p/5821192.html  转载 阅读目录 一.x-editable组件介绍 二.bootstrapTable行内编辑初始方案 ...

  8. Adnroid开发环境搭建(四步搞定)

    新手博友,多多关照 下面给大家介绍JDK Eclipse AndroidSDK ADT环境搭建,安装教程 第一步.安装JDK: 第二步.安装Eclipse: 第三步.下载并安装AndroidSDK: ...

  9. 小白Monkey学习笔记

    Monkey是google提供的一款对Android app进行压力测试工具,基于随机坐标位置,进行点击.滑动.输入等操作. Monkey的环境配置 pc电脑需要配置adb环境 Monkey程序由An ...

  10. vmWare pro 14.1.1+ubuntu-desktop-amd64的总体安装流程

    vmWare pro正常安装 下载后双击安装,按步骤走即可 创建虚拟机 设置虚拟机 window设置虚拟化技术 电脑重启后,弹出如下所示,选择 疑难解答->高级选项->UEFI固件设置-& ...