[GDKOI2023]错排

[GDKOI2023 提高组] 错排

题目描述

小 X 最近学习了错排问题,于是开始思考一个关于它的变种问题:有多少个长度为 n 的排列 p,满足对
im 的位置满足 pi>m,且对于所有位置 i 都满足 pii

小 X 一共想出了 T 个这样的问题,你能告诉他每个问题的答案吗?

由于答案可能过大,你只需要求出答案对 998244353 取模后的值即可。

输入格式

第一行输入一个整数 T,表示询问组数。

接下来的 T 行,每行输入两个整数 n,m

输出格式

输出 T 行,每行一个整数表示答案对 998244353 取模后的值。

样例 #1

样例输入 #1

6
8 0
8 4
100 10
1000 100
10000 1000
100000 10000

样例输出 #1

14833
576
548326276
694205000
493811811
135068319

提示

对于 100% 的数据,0T2×1050mn2×105

本题采用子任务捆绑测试。

  • Subtask 1 (1pts):保证 T=0
  • Subtask 2 (9pts):保证 T10n,m8
  • Subtask 3 (10pts):保证 m=0
  • Subtask 4 (20pts):保证 n,m5000
  • Subtask 5 (20pts):保证 T10
  • Subtask 6 (40pts):无特殊性质。

这题好像有很厉害的 O((n+m)n) 甚至 O(nlog2n) 做法,但是我不会。
首先 2m>n 显然无解。

f(i,j) 表示有 i 个位置,但是有 j 个位置钦定不一定要错开排的,ij 个位置一定要错开的方案数,gii 个数的错排数。

最后答案就是 (nm)!(n2m)!×f(nm,m)

考虑如何计算 f(i,j)。枚举有多少个数实际上是错排的,

f(i,j)=i=0mgi(mi)

将其写作 [xnm]g×(x+1)m,分块处理。用卷积预处理出 gi×(x+1)jB 的式子,查询时再乘上剩下的。复杂度是 O(qB+nBlogn),B 取 nlogn 的时候,复杂度 O(nnlogn)

#include<bits/stdc++.h>
using namespace std;
const int P=998244353,g=3,ivg=(P+1)/g,N=262144,B=3000;
int f[N],TME,r[N],dp[N],n,m,pw[100][N],pf[N],iv[N],inv[N],ans,C[B][B];
int pown(int x,int y)
{
	if(!y)
		return 1;
	int t=pown(x,y>>1);
	if(y&1)
		return 1LL*t*t%P*x%P;
	return 1LL*t*t%P;
}
const int ivN=pown(N,P-2);
void ntt(int a[],int op)
{
	for(int i=0;i<N;i++)
		if(r[i]<i)
			swap(a[i],a[r[i]]);
	for(int md=1;md<N;md<<=1)
	{
		int p=pown(op? g:ivg,(P-1)/(md<<1));
		for(int i=0;i<N;i+=md<<1)
		{
			for(int j=0,pw=1;j<md;j++,pw=1LL*pw*p%P)
			{
				int x=a[i+j+md]*1LL*pw%P;
				a[i+j+md]=(a[i+j]-x+P)%P;
				(a[i+j]+=x)%=P;
			}
		}
	}
	if(!op)
		for(int i=0;i<N;i++)
			a[i]=1LL*a[i]*ivN%P;
}
inline int read()
{
	int s=0;
	char ch=getchar();
	while(ch>'9'||ch<'0')
		ch=getchar();
	while(ch<='9'&&ch>='0')
		s=s*10+ch-'0',ch=getchar();
	return s;
}
int CC(int n,int m)
{
	return 1LL*f[n]*iv[m]%P*iv[n-m]%P; 
}
int main()
{
	//freopen("P10103_21.in","r",stdin);
//	freopen("a.out","w",stdout);
	for(int i=iv[0]=f[0]=inv[1]=1;i<N;i++)
	{
		r[i]=(r[i>>1]>>1)|(i&1)*N/2;
		f[i]=1LL*f[i-1]*i%P;
		if(i^1)
		{
			dp[i]=(dp[i-1]*1LL*i+(i&1? -1:1))%P;
			inv[i]=(P-P/i)*1LL*inv[P%i]%P;
		}
		iv[i]=iv[i-1]*1LL*inv[i]%P;
	}
	dp[0]=1;
	for(int i=0;i<B;i++)
		for(int j=0;j<=i;j++)
			C[i][j]=CC(i,j);
//	printf("%d\n",C(0,0));
	pf[0]=pf[1]=1;
	ntt(dp,1);
	memcpy(pw[0],dp,sizeof(dp)); 
	ntt(pf,1);
	for(int i=0;i<N;i++)
		pf[i]=pown(pf[i],B);
	for(int i=1;i*B<N;i++)
		for(int j=0;j<N;j++)
			pw[i][j]=pw[i-1][j]*1LL*pf[j]%P;
	for(int i=0;i*B<N;i++)
		ntt(pw[i],0);
	scanf("%d",&TME);
	while(TME--)
	{
		n=read(),m=read(),ans=0;
		if(2*m>n)
		{
			puts("0");
			continue;
		}
		int x=m%B;
		for(int j=0;j<=x&&j<=n-m;j++)
			(ans+=1LL*pw[m/B][n-m-j]*C[x][j]%P)%=P;
		printf("%lld\n",1LL*ans*f[n-m]%P*iv[n-2*m]%P);
	}
	return 0;
}
posted @   灰鲭鲨  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示