[GDKOI2023]错排
[GDKOI2023 提高组] 错排
题目描述
小 X 最近学习了错排问题,于是开始思考一个关于它的变种问题:有多少个长度为 的排列 ,满足对
于 的位置满足 ,且对于所有位置 都满足 ?
小 X 一共想出了 个这样的问题,你能告诉他每个问题的答案吗?
由于答案可能过大,你只需要求出答案对 取模后的值即可。
输入格式
第一行输入一个整数 ,表示询问组数。
接下来的 行,每行输入两个整数 。
输出格式
输出 行,每行一个整数表示答案对 取模后的值。
样例 #1
样例输入 #1
6
8 0
8 4
100 10
1000 100
10000 1000
100000 10000
样例输出 #1
14833
576
548326276
694205000
493811811
135068319
提示
对于 100% 的数据,,。
本题采用子任务捆绑测试。
- Subtask 1 (1pts):保证 。
- Subtask 2 (9pts):保证 ,。
- Subtask 3 (10pts):保证 。
- Subtask 4 (20pts):保证 。
- Subtask 5 (20pts):保证 。
- Subtask 6 (40pts):无特殊性质。
这题好像有很厉害的 甚至 做法,但是我不会。
首先 显然无解。
设 表示有 个位置,但是有 个位置钦定不一定要错开排的, 个位置一定要错开的方案数, 为 个数的错排数。
最后答案就是
考虑如何计算 。枚举有多少个数实际上是错排的,
将其写作 ,分块处理。用卷积预处理出 的式子,查询时再乘上剩下的。复杂度是 ,B 取 的时候,复杂度
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!