CF1528F AmShZ Farm

27 篇文章 0 订阅
16 篇文章 0 订阅

CF1528F

  • 我们要求 ∑ j ≤ i c j ≥ i \sum_{j\le i}c_j\ge i jicji n ! ∏ c i ! \frac{n!}{\prod c_i!} ci!n!的和。

  • 打表发现答案为 ( n + 1 ) n − 1 (n+1)^{n-1} (n+1)n1,考虑这是一个 n + 1 n+1 n+1个点的无根树的方案,考虑怎么将答案与一棵树构造一个双射。

  • 有一个很神奇的构造,我们有一个以1为根的大小为 n + 1 n+1 n+1的有标号无根树,我们在上面dfs,先走编号小的儿子,将走过每一个点的儿子个数写成一个序列,可以得到一个 n + 1 n+1 n+1长度的序列,由于最后一个一定是0,不妨删掉,那么恰好对应了一个 c i c_i ci的序列,显然 c i c_i ci满足条件。

  • 并且我们将 c i c_i ci按照dfs的顺序也可以还原一个无标号的有根树。

  • 考虑答案要求 c c c n ! ∏ c i ! \frac{n!}{\prod c_i!} ci!n!,将它与树的标号分配对应,由于根确定,所以剩下 n n n个点,又由于儿子顺序是固定的,因此方案数恰好是这个可重集合排列。

  • 剩下的只需要考虑这棵树的prufer序,分1和其他 n n n个点考虑,枚举它们的个数,对应prufer序的出现次数。将 i k i^k ik写成下降幂,与组合数消一消,推一下即可。最后NTT求斯特林数即可。


  • 一开始的 ( n + 1 ) n − 1 (n+1)^{n-1} (n+1)n1还有更加简单的推法。
  • 考虑 ∑ [ a j ≥ i ] ≤ n − i + 1 \sum [a_j\ge i]\le n-i+1 [aji]ni+1可以视为一个操作过程,有 n + 1 n+1 n+1个座位,每一次一个人从 a j a_j aj出发往后面走,直到找到第一个空位坐下,要求最后 n + 1 n+1 n+1没有坐人。
  • 为了满足对称性,我们需要将这个问题拓展一下,将座位变为循环座位,将 a j a_j aj变为 [ 1 , n + 1 ] [1,n+1] [1,n+1]。显然要求上面的方案数还是不变。此时可以发现最后剩下的位置的概率是均匀的,因此方案数显然是 ( n + 1 ) n − 1 (n+1)^{n-1} (n+1)n1
  • 进一步的我们如果固定某组数的个数,这组数值域 [ 1 , n + 1 ] [1,n+1] [1,n+1],剩下的随便选,最后空位的分布一样是均匀的,因此直接枚举,只需要求 ∑ i = 0 n ( n i ) n n − i i k \sum_{i=0}^n\binom{n}{i}n^{n-i}i^k i=0n(in)nniik,值域和概率的 n + 1 n+1 n+1抵消了。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define mo 998244353
#define maxn 262145
using namespace std;

int n,m,i,j,k;
ll fct[maxn],invf[maxn],a[maxn],b[maxn];

ll ksm(ll x,ll y){
	ll s=1;
	for(;y;y/=2,x=x*x%mo) if (y&1)
		s=s*x%mo;
	return s;
}

int lim,bt[maxn];
void dft(ll *a,int sig){
	for(int i=1;i<lim;i++) if (i<bt[i]) swap(a[i],a[bt[i]]);
	for(int mid=1;mid<lim;mid<<=1){
		ll gn=ksm(3,mo-1+sig*(mo-1)/(mid<<1));
		for(int j=0;j<lim;j+=mid<<1){
			ll g=1;
			for(int k=0;k<mid;k++,g=g*gn%mo){
				ll x=a[j+k],y=a[j+k+mid]*g;
				a[j+k]=(x+y)%mo,a[j+k+mid]=(x-y)%mo;
			}
		}
	}
	if (sig<0){
		ll inv=ksm(lim,mo-2);
		for(int i=0;i<lim;i++) a[i]=a[i]*inv%mo;
	}
}

int main(){
	freopen("ceshi.in","r",stdin);
	scanf("%d%d",&n,&m);
	fct[0]=1;for(i=1;i<maxn;i++) fct[i]=fct[i-1]*i%mo;
	invf[maxn-1]=ksm(fct[maxn-1],mo-2);
	for(i=maxn-2;i>=0;i--) invf[i]=invf[i+1]*(i+1)%mo;
	for(i=0;i<=m;i++) a[i]=ksm(i,m)*invf[i]%mo,b[i]=((i&1)?-1:1)*invf[i]%mo;
	lim=1; while (lim<=m*2) lim<<=1;
	for(i=1;i<lim;i++) bt[i]=(bt[i>>1]>>1)|((i&1)*(lim>>1));
	dft(a,1),dft(b,1);
	for(i=0;i<lim;i++) a[i]=a[i]*b[i]%mo;
	dft(a,-1);
	ll s=ksm(n+1,n),Inv=ksm(n+1,mo-2),ans=0;
	for(i=0;i<=m;i++) (ans+=s*a[i])%=mo,s=s*Inv%mo*(n-i)%mo;
	printf("%lld\n",(ans%mo+mo)%mo);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值