为什么 FFT 的 $O(n^2\log n)$ 可以过 $5000$

· · 题解

非官方做法,图一乐

想了 1h 没有别的 idea 写了个感觉过不了一点的 FFT \sout{O(n^2\log n)} 过了 /ll。早知道就直接写了。

题意简述

给你一个部分位置确定的排列,定义一个排列的代价为:

\sum\limits_{l=1}^n\sum\limits_{r=l}^n \text{MEX}(p_l,\dots,p_r)

问所有可能的排列的代价和。

Solution

考虑对每个 k 对满足 \text{MEX}(p_l,\dotsm,p_r)\ge k(p,l,r) 三元组数量求和。

定义 c(l,r) 表示 [l,r] 中还没确定的位置个数。

定义 h(l,r,k) 表示 [l,r] 的答案是否可以为 k

定义 q(k) 表示未确定的 \le k 的元素个数。

Ans=\sum\limits_{l=1}^n\sum\limits_{r=l}^n\sum\limits_{k=1}^{n} h(l,r,k)\binom{c(l,r)}{q(k)}q(k)!(c(1,n)-q(k))!

考虑优化

剩下的情况发现 $h(l,r,k)=1$ 是只需要 $[l',r']\subset [l,r]$,其中 $[l',r']$ 是最小满足每个确定的 $\le k$ 的元素位置都包含的区间。 然后修改一下式子 $$ \sum\limits_{l=1}^{l'}\sum\limits_{r=r'}^{n}\sum\limits_{k=1}^n \binom{c(l,l'-1)+c(l',r')+c(r'+1,r)}{q(k)}q(k)!(c(1,n)-q(k))!\\ $$ 容易发现可以用 FFT 优化。 时间复杂度 $O(n^2\log n)$,我也不知道为什么能过。 ```cpp #include<bits/stdc++.h> #define cir(i,a,b) for(int i=a;i<b;++i) using namespace std; using lint=long long; static constexpr auto MOD=(int)(1e9+7); class mathbase{ private: vector<lint> fct,ifct; public: constexpr auto qpow(lint a,auto b) const{ auto res=1ll; while(b){ if(b&1) (res*=a)%=MOD; (a*=a)%=MOD;b>>=1; } return res; } constexpr auto inv(auto x) const{return qpow(x,MOD-2);} auto init(const auto n){ fct.resize(n,1); cir(i,1,n) fct[i]=fct[i-1]*i%MOD; ifct.resize(n); ifct[n-1]=inv(fct[n-1]); for(auto i=n-2;~i;--i) ifct[i]=ifct[i+1]*(i+1)%MOD; } auto C(const auto a,const auto b) const{ if(a<b||b<0) return 0ll; return fct[a]*ifct[b]%MOD*ifct[a-b]%MOD; } auto fact(const auto x) const{return x>-1?fct[x]:0;} } math; using complf=complex<double>; class basic_poly{ private: static constexpr auto pi=acosl(-1); auto change(vector<complf>&a,const int n){ vector<int> rev(n); cir(i,0,n) rev[i]=((rev[i>>1]>>1)|((n>>1)*(i&1))); cir(i,0,n) if(i<rev[i]) swap(a[i],a[rev[i]]); } template<const int type> auto fft(vector<complf>&a,const int n){ change(a,n); for(auto h=2;h<n+1;h<<=1){ const auto midh=h/2; const auto wx=complf(cos(pi*2/h),sin(pi*2*type/h)); for(auto i=0;i<n;i+=h){ auto u=complf(1,0); cir(k,i,i+midh){ const auto wy=u*a[k+midh]; a[k+midh]=a[k]-wy;a[k]+=wy; u*=wx; } } } if(type==-1) for(auto&i:a) i/=n; } public: auto mul(vector<lint> a,vector<lint> b){ const auto n=1<<((int)(log2(a.size()+b.size()))+1); vector<complf> fx(n); cir(i,0,(int)(a.size())) fx[i]+=complf(a[i],0); cir(i,0,(int)(b.size())) fx[i]+=complf(0,b[i]); fft<1>(fx,n); for(auto&i:fx) i*=i; fft<-1>(fx,n); vector<lint> res(n); cir(i,0,n) res[i]=roundl(fx[i].imag()/2); return res; } } poly; static constexpr auto maxn=5007; int main(){ ios::sync_with_stdio(false),cin.tie(nullptr); int T;cin>>T; math.init(maxn); while(T--) [&]{ int n;cin>>n;vector<int> a(n); for(auto&i:a) cin>>i; const auto m=ranges::count(a,-1); vector<lint> cur(m+1); vector<lint> qans(m+1); cir(i,0,n){ if(a[i]==-1){ for(auto i=m;i;--i) (cur[i]+=cur[i-1])%=MOD; (++cur[1])%=MOD; } ++cur[0]; cir(i,0,m+1) (qans[i]+=cur[i])%=MOD; } auto ans=0ll; vector<int> pos(n,-1); cir(i,0,n) if(a[i]>-1) pos[a[i]]=i; cir(w,1,n+1){ auto l=n,r=0,cnt=0; cir(x,0,w) if(pos[x]>-1){ l=min(l,pos[x]); r=max(r,pos[x]); }else{ ++cnt; } if(l>r){ cir(c,cnt,cnt+1) (ans+=qans[c]*math.fact(m-c)%MOD*math.fact(c))%=MOD; }else{ auto mc=0; cir(i,l,r+1) mc+=(a[i]==-1); vector<lint> al(l+1),ar(n-r+1); auto wcnt=0; al[0]=ar[0]=1; for(auto i=l-1;~i;--i) wcnt+=(a[i]==-1),++al[wcnt]; wcnt=0; cir(i,r+1,n) wcnt+=(a[i]==-1),++ar[wcnt]; const auto res=poly.mul(al,ar); cir(i,0,(int)(res.size())) if(res[i]) (ans+=(res[i]%MOD)*math.C(i+mc,cnt)%MOD*math.fact(m-cnt)%MOD*math.fact(cnt))%=MOD; } } cout<<ans<<'\n'; }(); return 0; } ``` 令人忍俊不禁。