题解:P8478 「GLR-R3」清明

· · 题解

考试数据分治写挂了,,还发现 O(n3^{n/2})O(n^32^{n/2}) 快。

写出生成函数:

=\left[\prod x_i^{a_i}\right]\left.\prod _i\frac{\partial}{\partial y}\prod_{i-m\le k\le i}\frac{1}{1-x_ky}\right|_{y=1}\\ =\left[\prod x_i^{a_i}\right]\prod_i\prod_{i-m\le j\le i}\frac{1}{1-x_j}\sum_{i-m\le k\le i}\frac{x_k}{1-x_k}\\

F_i=\prod_{j\le i} \frac{1}{1-x_j}

ans=\left[\prod x_i^{a_i}\right]\left(\prod_{n-m\le i\le n}F_i\right)\left(\prod _i \sum_{i-m\le k\le i}\frac{x_k}{1-x_k}\right)

后半部分相当于每个 i 选择一个 k,然后贡献只跟每个 k 被选择的次数相关,且事实上等于乘积,即设被选择次数为 c_{1:n},造成贡献是

\prod_i f_{i,c_i} 当 $m\le \frac n2$ 时,直接状压 dp 并使用子集卷积获得 $O(n^32^{n/2})$ 的复杂度。当 $m>\frac n2$ 时,对后 $n-m-1$ 个位置状压,前面的 $m+1$ 位置 $i$ 可以钦定任意 $j\le i$ ,因此前面部分可以背包记录。这部分的复杂度也是 $O(n^32^{n/2})$。 最终复杂度 $O(n^32^{n/2})$,实际上子集卷积不如暴力转移快。 ```cpp #include<bits/stdc++.h> using namespace std; #define int long long const int mod=998244353; namespace FWT{ // #define Poly vector<int> int n,A[(1<<17)+5],B[(1<<17)+5],C[(1<<17)+5]; void work(){ int sz=0,len=1<<n; for(int S=0;S<len;++S) sz+=(A[S]>0); for(int S=0;S<len;++S) sz-=(B[S]>0); if(sz>0) for(int i=0;i<len;++i) swap(A[i],B[i]); for(int S=0;S<len;++S) if(A[S]){ int v=A[S],TT=len-S-1; for(int T=TT;~T;T=(T-1)&TT){ if(B[T]) C[S+T]=(C[S+T]+v*B[T])%mod; if(!T) break; } } } void clear(){ for(int j=0;j<(1<<FWT::n);j++)FWT::A[j]=FWT::B[j]=FWT::C[j]=0; } } int f[40][40],F[40][(1<<17)+5],n,m,a[40],g[2][40][(1<<16)+5]; int qp(int a,int b){ if(b==0)return 1; int T=qp(a,b>>1);T=T*T%mod; if(b&1)T=T*a%mod; return T; } int C(int a,int b){ int z=1; for(int i=a;i>=a-b+1;i--)z=z*i%mod; for(int i=1;i<=b;i++)z=z*qp(i,mod-2)%mod; z=(z%mod+mod)%mod; return z; } inline int pop(int x){return __builtin_popcount(x);} int high[(1<<17)+5],Ci[40][40]; int CL(int i){return min(n-i+1,m+1);} signed main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); int N=35; for(int i=0;i<=N;i++){ Ci[i][0]=1; for(int j=1;j<=i;j++)Ci[i][j]=(Ci[i-1][j]+Ci[i-1][j-1])%mod; } cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; int b; if(i<n-m)b=m; else b=n-i; for(int j=0;j<=min(m+1,a[i]);j++)f[i][j]=C(a[i]+b,b+j); } if(m==0){ int ans=1; for(int i=1;i<=n;i++)ans=ans*f[i][1]%mod; cout<<ans<<endl; }else if(m<=14){ for(int j=0;j<(1<<m+1);j++){ high[j]=-1; for(int k=0;k<m+1;k++)if(j&(1<<k))high[j]=k; } F[0][0]=1; for(int i=1;i<=n;i++){ FWT::n=m+1; for(int j=0;j<(1<<m+1);j++)FWT::A[j]=FWT::B[j]=FWT::C[j]=0; for(int j=0;j<(1<<m+1);j++){ (FWT::A[j>>1]+=F[i-1][j])%=mod; if(high[j]+i<=n)FWT::B[j]=f[i][pop(j)]; } FWT::work(); for(int j=0;j<(1<<m+1);j++)F[i][j]=FWT::C[j]; for(int j=0;j<(1<<m+1);j++)if(!(j&1))F[i][j]=0; } int ans=0; for(int i=0;i<(1<<m);i++)(ans+=F[n][i])%=mod; cout<<ans<<endl; }else{ F[n+1][0]=1; for(int i=n;i>=m+2;i--){ int cl=min(n-i+1,m+1); FWT::n=cl; for(int j=0;j<(1<<cl);j++){ FWT::B[j]=f[i][pop(j)]; if(!(j&1))FWT::A[j]=F[i+1][j>>1]; } FWT::work(); for(int j=0;j<(1<<cl);j++)F[i][j]=FWT::C[j]; FWT::clear(); } int cur=0,lst=CL(m+2); for(int j=0;j<(1<<CL(m+2));j++)g[0][0][j]=F[m+2][j]; for(int i=m+1;i>=1;i--){ int cl=min(n-m-1,i-1),c2=m+1-i; for(int j=0;j<=c2;j++)for(int k=0;k<(1<<cl);k++){ if(lst>cl)g[cur][j][k]=0; } for(int j=0;j<=c2;j++)for(int k=0;k<(1<<lst);k++){ if(k>=(1<<cl)){ if((k|((1<<cl)-1))!=(1<<lst)-1)continue; (g[cur][j][k&((1<<cl)-1)]+=g[cur][j][k])%=mod; } } for(int j=0;j<(1<<cl);j++)for(int k=0;k<=m+2-i;k++)g[cur^1][k][j]=0; for(int j=0;j<=c2;j++){ for(int k=0;k<=m+2-i-j;k++){ FWT::n=cl; for(int t=0;t<(1<<cl);t++){ FWT::B[t]=f[i][pop(t)+k]; FWT::A[t]=g[cur][j][t]; } FWT::work(); for(int t=0;t<(1<<cl);t++){ (g[cur^1][j+k][t]+=FWT::C[t]*Ci[m+2-i-j][k])%=mod; } FWT::clear(); } } lst=cl; cur^=1; } int ans=g[cur][m+1][0]; cout<<ans<<endl; } return 0; } ``` 代码的 $m>14$ 部分可能在搬过来处理 $m\le 14$ 的时候会出现问题。