Cnoi2020 四角链の题解

前面的唐性质自己想

阅读题意可得,格子的编号为 \(1,2,3,…,n\) ,数字的集合为 \({1,2,…,n-1}\)

在合法方案中,有 \(k\) 个格子填了数,所以有 \(k\) 个数字被使用,那么一共有 \((n-1)-k\) 个数字没有用

\(k\) 个数和 \(k\) 个字符一一对应,每个数字 \(x\) 必须放在 \(\ge x\) 的格子上

但数字放在格子上后,格子本身没有额外的区分

换个角度看:

\(k\) 个被选的数字集合为 \(D\) ,每个数字 \(d\) 放在格子 \(f(d)\)\(f\) 是单射到 被选中的格子集合 \(G\)

我们观察格子编号 \(\ge\) 数字的条件。

假设所有格子都可用,即为任意一个数字 \(d\) 可以选 \(d,d+1,d+2,…,n\) 中任意格子

这等价于分配问题,即为将数字 \(1,2,3,…,n-1\) 分配到 \(n-k\) 个块内

这里大神Mr_RedStone已经想到是第二类斯特林数了,可是为了向我一样的蒟蒻,我们还是一步步证明

我们增设一个数字 \(n\) 和一个格子 \(n\) ,并令虚拟数字只能放进虚拟格子

这个问题是典型的计数题目中的 \(rook polynomials\) 类的题目

根据结论,答案数为 \(S(n,k)\)

代码

#include<bits/stdc++.h>
#define Code using
#define by namespace
#define jhc std
Code by jhc;
#define int long long
int in()
{
	int k=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')k=k*10+c-'0',c=getchar();
	return k*f;
}
void out(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x<10)putchar(x+'0');
	else out(x/10),putchar(x%10+'0');
}
const int N=1e6+10,mod=998244353;
int jc[N],inv[N];
void init()
{
	inv[0]=inv[1]=jc[0]=1;
	for(int i=2;i<N;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<N;i++)inv[i]=inv[i-1]*inv[i]%mod,jc[i]=jc[i-1]*i%mod;
}
int ksm(int a,int b)
{
	int res=1;
	for(;b;a=a*a%mod,b>>=1)if(b&1)res=res*a%mod;
	return res;
}
int C(int n,int m){return jc[n]*inv[m]%mod*inv[n-m]%mod;}
int S(int n,int k)
{
	int sum=0;
	for(int i=1;i<=k;i++)
		if((k-i)&1)sum=(sum+mod-C(k,i)*ksm(i,n)%mod)%mod;
		else sum=(sum+C(k,i)*ksm(i,n)%mod)%mod;
	return sum*inv[k]%mod;
}
signed main()
{
	init();
	int n=in(),k=in();
	out(S(n,n-k));
	return 0;
}
posted @ 2026-02-27 15:44  jianghaochen  阅读(16)  评论(0)    收藏  举报