专栏文章
题解:AT_agc064_f [AGC064F] No Permutations
AT_agc064_f题解参与者 3已保存评论 7
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 7 条
- 当前快照
- 1 份
- 快照标识符
- @miqqcqet
- 此快照首次捕获于
- 2025/12/04 09:01 3 个月前
- 此快照最后确认于
- 2025/12/04 09:01 3 个月前
upd on 2025.1.1:式子出现了一些打错的地方。
思路
搬运一下官方题解做法。
直接做肯定没法做,考虑容斥。
记所有长度为 的区间的集合 ,设 ,定义 表示 乘上满足“ 中任意区间都是 的排列”的方案数。下面如无特殊说明,区间全部左闭右开。
则答案为 。
当 时,,也就是把相同值元素看作不同再除以每种方案重复算的次数。下面只讨论 的情况。
定义两个区间 相交当且仅当 ,注意 的时候也是相交。定义一个连通块是一个区间的集合 ,满足对于其中任意两个区间 ,都存在一个区间的序列 ,其中 与 相交。此时称 是连通的,其长度为区间 的长度,记为 。
直接计算所有 (下记为 ,即 是 的幂集的元素)的贡献不好算,我们把 划分成两个集合分别计算贡献。如无特殊说明,下面默认 ,全集是 。
设 是满足以下条件的 的集合:存在一个区间 ,满足对于所有 , 相交。再设 是 的补集。
那么下面就剩下了两个问题:计算 和 。
计算
设 , 中区间左端点分别为 ,不难发现 ,则排列的方案数有以下几部分:
-
是 的排列,方案数 。
-
是区间 的排列,方案数 。
-
是区间 的排列,方案数 。
-
依此类推, 是区间 的排列,方案数 。
-
不被任何区间包含的 个元素,方案数 。这是因为总共有 种方案,其中有 对相同元素会算重。
全部乘起来,把容斥系数拆到求积号里面,有 。
枚举 求和得到 。
问题转化为求 。
观察发现 是一组 的拆分,可以用生成函数描述。但是生成函数只是无脑加起来,并不能满足 的限制。所以我们考虑放宽限制。
设 表示连通的 的集合。容易得到 。因此要求 。
先来解决式子的第一项。正如刚刚说的,求积号里是一个拆分,可以用生成函数描述。
设 ,则 。这里 是 的选法。
求 可以用多项式求逆 求出来。
接下来就是本题最难的部分,求 。
我们先考虑 长什么样子。设 最左边的区间是 ,最右边是 。你发现,由于每个区间长为 ,因此任意一个 中区间都会和 中恰好一个相交。这是因为如果和两个相交就属于 了,和两个都不相交长度就不可能到达 。
于是,设和 相交的区间的集合是 ,和 相交的区间的集合是 。显然, 都是连通的。 的长度是 ,区间左端点是 ; 的长度是 ,区间左端点是 ; 是 的交的长度,即 。则,。不妨令 的三元组的集合是 ,即 。三元组中 是最小的。
所以有 。这里前面没有负号是因为 的负号消掉了。
类似于上面的讨论,我们有该式等于 。
枚举 变为枚举 ,得到该式等于 。 是 的选法。
不妨设 。我们需要快速求出 。这里前面的负号是为了把之前的式子前面的减变成加。
考虑分治。设
solve(l,r) 表示将满足 的所有 的贡献加进 ,再设 。一共有三种贡献:-
或 时,可以递归下去。
-
时, 可以任取 的值,与 无关。我们要对于每个 求出 。可以先把 做和卷积,再把结果和 做差卷积,用 NTT 做到 。
-
时,由于 是对称的,不妨令 ,答案乘 即可,这是因为 必不可能满足 。因此有 ,先把 做差卷积,再和 做和卷积即可 。
时间复杂度 。
计算
类似于上面的讨论,设 最左边的区间是 ,最右边是 ,任意一个 中区间都会和 中恰好一个相交。设和 相交的区间的集合是 ,和 相交的区间的集合是 。显然, 都是连通的。 的长度是 ,区间左端点是 ; 的长度是 ,区间左端点是 ; 是 的距离,即 。
排列的方案数有以下几部分:
-
不被 包含的部分,一共 种方案。
-
包含的部分:
-
是不在 元素的排列,方案数 。
-
是 元素的排列,方案数 。
-
依此类推, 是区间 的排列,方案数 。
-
-
包含的部分,类似地,方案数 。
拆一下容斥系数,有 。
枚举 变成枚举 ,结合 的定义,我们有 。
化简即为 。 是选择 。
可以前缀和优化做到 计算。
综上,我们以 的时间复杂度解决了这个问题。
代码
CPP#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;
}
相关推荐
评论
共 7 条评论,欢迎与作者交流。
正在加载评论...