题解:AT_agc064_f [AGC064F] No Permutations
littlez_meow · · 题解
upd on 2025.1.1:式子出现了一些打错的地方。
思路
搬运一下官方题解做法。
直接做肯定没法做,考虑容斥。
记所有长度为
则答案为
当
定义两个区间
直接计算所有
设
那么下面就剩下了两个问题:计算
计算 \sum\limits_{S\in P}f(S)
设
-
-
-
-
依此类推,
[l_{k-1}+n,l_k+n) 是区间[l_{k-1},l_k) 的排列,方案数(l_k-l_{k-1})! 。 -
不被任何区间包含的
2n-L 个元素,方案数\rho(2n-L)=\dfrac{(2n-L)!}{2^{\max(n-L,0)}} 。这是因为总共有(2n-L)! 种方案,其中有\max(n-L,0) 对相同元素会算重。
全部乘起来,把容斥系数拆到求积号里面,有
枚举
问题转化为求
观察发现
设
先来解决式子的第一项。正如刚刚说的,求积号里是一个拆分,可以用生成函数描述。
设
求
接下来就是本题最难的部分,求
我们先考虑
于是,设和
所以有
类似于上面的讨论,我们有该式等于
枚举
不妨设
考虑分治。设 solve(l,r) 表示将满足
时间复杂度
计算 \sum\limits_{S\in Q}f(S)
类似于上面的讨论,设
排列的方案数有以下几部分:
-
不被
[u,u+n),[v,v+n) 包含的部分,一共n! 种方案。 -
* $[u_p,u+n)$ 是不在 $[u+n,u+n+x)$ 元素的排列,方案数 $(n-x)!$。 * $[u_{p-1},u_p)$ 是 $[u_{p-1}+n,u_p+n)$ 元素的排列,方案数 $(u_p-u_{p-1})!$。 * 依此类推,$[u_0,u_1)$ 是区间 $[u_0+n,u_1+n)$ 的排列,方案数 $(u_1-u_0)!$。 -
拆一下容斥系数,有
枚举
化简即为
可以前缀和优化做到
综上,我们以
代码
#include<bits/stdc++.h>
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define vi vector<int>
using namespace std;
const int MAXN=1<<20,MOD=998244353,INV2=499122177,INV6=166374059,G=3;
inline ll qpow(ll base,int expo){
ll res(1);
while(expo){
(expo&1)&&(res=res*base%MOD);
base=base*base%MOD,expo>>=1;
}
return res;
}
const int INVG=qpow(G,MOD-2);
int gpow[21],invgpow[21];
inline void calc(){
F(i,1,20) gpow[i]=qpow(G,(MOD-1)>>i),invgpow[i]=qpow(INVG,(MOD-1)>>i);
return;
}
inline void meow(int&t){
t<0&&(t+=MOD);
t>=MOD&&(t-=MOD);
return;
}
int rev[MAXN];
inline void NTT(int*poly,int len,bool inv){
F(i,0,len-1) (i<rev[i])&&(swap(poly[i],poly[rev[i]]),1);
static ll g[MAXN];
g[0]=1;
for(int i(1),expo(1);i<len;i<<=1,++expo){
ll omega=inv?invgpow[expo]:gpow[expo];
F(j,1,i-1) g[j]=g[j-1]*omega%MOD;
for(int j(0);j<len;j+=(i<<1)) F(k,0,i-1){
int&x(poly[j|k]),&y(poly[i|j|k]);
ll qwq(g[k]*y%MOD);
y=x-qwq;
y<0&&(y+=MOD);
x+=qwq;
x>=MOD&&(x-=MOD);
}
}
if(inv){
ll invl=qpow(len,MOD-2);
F(i,0,len-1) poly[i]=poly[i]*invl%MOD;
}
return;
}
inline void build_rev(int n){
F(i,0,n-1) rev[i]=(rev[i>>1]>>1)|((i&1)?(n>>1):0);
return;
}
struct poly{
int num[MAXN]={};
int len=0;
inline void resize(const int a){
for(;len>a;--len) num[len]=0;
len=a;
if(len<0) len=0;
return;
}
inline poly operator+(const poly a)const{
poly res;
res.len=max(a.len,len);
F(i,0,res.len) res.num[i]=num[i]+a.num[i],meow(res.num[i]);
return res;
}
inline poly operator+(const int a)const{
poly res=*this;
int&qwq(res.num[0]);
qwq+=a;
meow(qwq);
return res;
}
inline poly operator-(const poly a)const{
return a*(-1)+*this;
}
inline poly operator-(const int a)const{
return *this+(-a);
}
inline poly operator*(const poly a)const{
poly x,y=*this;
if(a.len*1ll*len<=1e5){
x.len=a.len+len;
F(i,0,len) F(j,0,a.len){
int&qwq(x.num[i+j]);
qwq=(qwq+a.num[j]*1ll*num[i])%MOD;
}
return x;
}
x=a;
int expo=max(__lg(((len+a.len+1)<<1)+1)+1,1),l=1<<expo;
build_rev(l);
NTT(x.num,l,0);
NTT(y.num,l,0);
F(i,0,l-1) x.num[i]=1ll*x.num[i]*y.num[i]%MOD;
NTT(x.num,l,1);
x.resize(len+a.len);
return x;
}
inline poly operator*(const int a)const{
poly res=*this;
F(i,0,len){
int&qwq(res.num[i]);
qwq=a*1ll*qwq%MOD;
meow(qwq);
}
return res;
}
inline poly inv(){
poly res;
res.num[0]=qpow(num[0],MOD-2);
for(int l(2),expo(1);l<(len<<1);l<<=1,++expo){
int tmp[MAXN]={};
F(i,0,(l<<1)-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<expo);
memcpy(tmp,num,sizeof(int)*l);
NTT(tmp,l<<1,0);
NTT(res.num,l<<1,0);
F(i,0,(l<<1)-1){
int&qwq(res.num[i]),t(2-1ll*qwq*tmp[i]%MOD);
meow(t);
qwq=1ll*qwq*t%MOD;
}
NTT(res.num,l<<1,1);
F(i,l,(l<<1)-1) res.num[i]=0;
}
res.resize(len);
return res;
}
};
int px[MAXN],py[MAXN];
inline vi mul(const vi&x,const vi&y){
int n=x.size()+y.size()-1;
n=1<<(__lg(n)+1);
build_rev(n);
F(i,0,x.size()-1) px[i]=x[i];
F(i,x.size(),n-1) px[i]=0;
F(i,0,y.size()-1) py[i]=y[i];
F(i,y.size(),n-1) py[i]=0;
NTT(px,n,0),NTT(py,n,0);
F(i,0,n-1) px[i]=px[i]*1ll*py[i]%MOD;
NTT(px,n,1);
vi res(x.size()+y.size()-1);
F(i,0,res.size()-1) res[i]=px[i];
return res;
}
int n;
ll fact[MAXN],B[MAXN];
poly w;
inline ll rho(int L){
return fact[2*n-L]*qpow(INV2,max(n-L,0))%MOD;
}
inline void m_le_xy(int l,int m,int r){
vector<int>x(r-l+1),y(r-l+1);
F(i,m,r-1) (x[i-l]+=w.num[i])>=MOD&&(x[i-l]-=MOD);
F(i,l,m-1) y[r-i]=fact[n-i];
x=mul(x,x);
x=mul(x,y);
F(i,max(r-2*l,0),x.size()-1){
int qwq=i-r+l*2;
if(qwq>n) break;
(B[qwq]+=x[i])>=MOD&&(B[qwq]-=MOD);
}
return;
}
inline void x_le_m_le_y(int l,int m,int r){
vector<int>x(r-l+1),y(r-l+1);
F(i,l,m-1) (x[i-l]+=w.num[i])>=MOD&&(x[i-l]-=MOD);
F(i,l,m-1) y[r-i]=fact[n-i];
x=mul(x,y);
F(i,0,min(int(x.size()-1),r-l)) x[i]=0;
F(i,0,r-l) y[i]=0;
F(i,m,r-1) y[i-l]=w.num[i];
x=mul(x,y);
F(i,max(r-2*l,0),x.size()-1){
int qwq=i-r+l*2;
if(qwq>n) break;
(B[qwq]+=x[i])>=MOD&&(B[qwq]-=MOD);
(B[qwq]+=x[i])>=MOD&&(B[qwq]-=MOD);
}
return;
}
void solve(int l,int r){
if(r-l<=1) return;
int m=(l+r)>>1;
solve(l,m),solve(m,r);
m_le_xy(l,m,r);
x_le_m_le_y(l,m,r);
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
calc();
cin>>n;
fact[0]=1;
F(i,1,3*n) fact[i]=fact[i-1]*i%MOD;
F(i,0,n) w.num[i]=fact[i];
w.len=2*n+1;
w=w.inv();
int ans=fact[3*n]*qpow(INV6,n)%MOD;
F(L,0,2*n) ans=(ans+rho(L)*(MOD-fact[n])%MOD*(2*n-L+1)%MOD*w.num[L])%MOD;//S in P'
solve(0,n);
F(L,n,2*n) ans=(ans+rho(L)*(MOD-fact[n])%MOD*(2*n-L+1)%MOD*B[L-n])%MOD;//S in P'\P
ll sum=0;
F(z,1,n){//S in Q
sum=(sum+w.num[z-1]*fact[n-z+1])%MOD;
ans=(ans+fact[n]*(n-z+1)%MOD*sum%MOD*sum)%MOD;
}
cout<<ans;
return 0;
}