JOISC2018D 修行 (Asceticism)

题意

给定 n,k,求能被恰好分成 k 段上升序列,即恰好存在 kpi>pi+1 的位置 i 的排列 p 的个数,模数 109+7

n,k105

分析

其实就是欧拉数

有一个 O(n2) 做法:设 fn,k 表示答案。转移考虑 n 插入在上升序列末尾(贡献 0)或者其他地方(贡献 1),fn,k=kfn1,k+(nk+1)fn1,k1。对正解没有启发作用。

考虑二项式反演,转而计算钦定 k 个位置 pi>pi+1 的方案数,这 k 个大于号能将原排列分成 nk 段,段内序列递减。发现这个问题等价于将 n 个有标号的球划分到 nk 个有标号盒子里,盒子之内的球没有顺序。这其实就是第二类斯特林数乘以阶乘 S(n,nk)(nk)!。考虑将 S(n,nk) 用通项展开:

fk=i=0nk(1)nki(nki)in

代入二项式反演式子(至少推恰好) gk=i=kn(1)ik(ik)fi

gk=i=kn(1)ik(ik)j=0ni(1)nij(nij)jn

=j=0n(1)nkjjni=0nj(ik)(nij)

=j=0n(1)nkjjn(n+1k+j+1)

第三步用到了一个组合恒等式 i=0n(ix)(niy)=(n+1x+y+1),证明考虑枚举一个特殊位置,然后让前面的选 x 个,后面的选 y 个,这等价于 n+1 个物品中选 x+y+1 个。

直接做即可,复杂度 O(nlogn)O(n)

多点求值可以套用 fk=S(n,nk)(nk)! 直接第二类斯特林数行做。

int n,k;
int fac[maxn],inv[maxn];
int ksm(int x,int y){
	int res=1;
	for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;
	return res;
}
int C(int x,int y){
	if(x<y)return 0;
	return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
void init(int lim){
	fac[0]=1;rep(i,1,lim)fac[i]=fac[i-1]*i%mod;
	inv[lim]=ksm(fac[lim],mod-2);per(i,lim-1,0)inv[i]=inv[i+1]*(i+1)%mod;
}
inline void solve_the_problem(){
	n=rd(),k=rd()-1;
	init(n+1);
	int ans=0;
	rep(i,0,n-k){
		int res=ksm(i,n)*C(n+1,k+i+1)%mod;
		if((n-k-i)%2==0)ans=(ans+res)%mod;
		else ans=(ans+mod-res)%mod;
	}
	write(ans);
}

作者:dcytrl

出处:https://www.cnblogs.com/dcytrl/p/18724528

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   dcytrl  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示