[51nod1514][NTT][多项式求逆]美妙的序列

6 篇文章 0 订阅
1 篇文章 0 订阅

Description

某个1~n的排列如果满足:

在1~n-1 这些位置后面将序列断开,使得总可以从右边找到一个数,并且该数不大于左边的所有数,则称该序列为“美妙的”。

给出n,求长度为n的“美妙的序列”的数量。

例如:n为3时有3种

2 3 1

3 1 2

3 2 1

解释:比如 2 3 1

(2) (3 1) 1比2小

(2 3) (1) 1比2小

都满足上面的条件。

3 2 1

(3)(2 1) 1比3小

(32)(1) 1比3小

都满足上面的条件。

而2 1 3不满足,因为(2 1)(3),3比左边所有的数都大。

求长度为n的美妙序列的数量(mod 998244353)

Input

第一行一个数T,表示数据组数(T<=10W) 接下来T行,每行一个数N(N<=10W)

Output

对于每组询问输出答案(mod 998244353)

Sample Input

1
3

Sample Output

3

题解

这个朴素dp还是很好想的啊
发现没有办法直接计算合法的,用不合法的减去
确定第一个让他不合法的位置,容易发现这个位置前面的值域连续,后面的值域也连续
且都为 [ 1 , i ] [1,i] [1,i] [ i + 1 , n ] [i+1,n] [i+1,n]
前面的一定要合法,后面的任意排列可以知道转移
f [ i ] = i ! − ∑ f [ j ] ∗ ( i − j ) ! f[i]=i!-\sum f[j]*(i-j)! f[i]=i!f[j](ij)!
可知
∑ f [ j ] ∗ ( i − j ) ! = i ! \sum f[j]*(i-j)!=i! f[j](ij)!=i!
上式将 f [ i ] f[i] f[i] 0 ! 0! 0!合并了…
显然这是一个卷积形式,考虑如何优化
构造生成函数 f ( x ) = 1 + 1 ! x + 2 ! x 2 + 3 ! x 3 . . . f(x)=1+1!x+2!x^2+3!x^3... f(x)=1+1!x+2!x2+3!x3...
构造生成函数 g ( x ) = f [ 0 ] + f [ 1 ] x + f [ 2 ] x 2 + f [ 3 ] x 3 . . . g(x)=f[0]+f[1]x+f[2]x^2+f[3]x^3... g(x)=f[0]+f[1]x+f[2]x2+f[3]x3...
容易得到
f ( x ) ∗ g ( x ) + 1 = f ( x ) f(x)*g(x)+1=f(x) f(x)g(x)+1=f(x)
这个1是用来补足常数项的
移项可知
g ( x ) = 1 − 1 f ( x ) g(x)=1-\frac{1}{f(x)} g(x)=1f(x)1
这个可以多项式求逆求出
或者你根据 ∑ f [ j ] ∗ ( i − j ) ! = i ! \sum f[j]*(i-j)!=i! f[j](ij)!=i!可以分治NTT做
还是点技能舒服

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define mod 998244353
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void pr1(int x){write(x);printf(" ");}
inline void pr2(int x){write(x);puts("");}
LL pow_mod(LL a,LL b)
{
	LL ret=1;
	while(b)
	{
		if(b&1)ret=ret*a%mod;
		a=a*a%mod;b>>=1;
	}
	return ret;
}
int R[110000*4],L;
void NTT(LL *y,int len,int on)
{
	for(int i=0;i<len;i++)if(i<R[i])swap(y[i],y[R[i]]);
	for(int i=1;i<len;i<<=1)
	{
		LL wn=pow_mod(3,(mod-1)/(i<<1));if(on==-1)wn=pow_mod(wn,mod-2);
		for(int j=0;j<len;j+=(i<<1))
		{
			LL w=1;
			for(int k=0;k<i;k++)
			{
				LL u=y[j+k];
				LL v=y[j+k+i]*w%mod;
				y[j+k]=(u+v)%mod;
				y[j+k+i]=(u-v+mod)%mod;
				w=w*wn%mod;
			}
		}
	}
	if(on==-1)
	{
		LL temp=pow_mod(len,mod-2);
		for(int i=0;i<len;i++)y[i]=(y[i]*temp)%mod;
	}
}
LL A[110000*4],G[110000*4],B[110000*4];
void getinv(LL *B,int len)
{
	if(len==1){B[0]=pow_mod(A[0],mod-2);return ;}
	getinv(B,len>>1);
	int ln;L=0;
	for(ln=1;ln<=len;ln<<=1)L++;
	for(int i=0;i<ln;i++)R[i]=(R[i>>1]>>1)|(i&1)<<(L-1);
	for(int i=0;i<len;i++)G[i]=A[i];
	for(int i=len;i<ln;i++)G[i]=B[i]=0;
	NTT(G,ln,1);NTT(B,ln,1);
	for(int i=0;i<ln;i++)B[i]=(B[i]*(2-G[i]*B[i]%mod)+mod)%mod;
	NTT(B,ln,-1);
	for(int i=len;i<ln;i++)B[i]=0;
}
int main()
{
	int length;
	for(length=1;length<=100000;length<<=1);
	A[0]=1;
	for(int i=1;i<length;i++)A[i]=A[i-1]*i%mod;
	getinv(B,length);
 	for(int i=0;i<length;i++)B[i]=(-B[i]+mod)%mod;B[0]++;
	int T=read();
	while(T--)pr2(B[read()]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值