题解:CF1988F Heartbeat
首先对于每种前缀最大值个数,后缀最大值个数,上升点个数,可以先求出其对应的排列的方案数,乘上对应的代价后加起来就是答案。
注意到一个排列的最大值前面只有前缀最大值,后面只有后缀最大值,因此可以将前缀最大值和后缀最大值分开处理,最后再合并。
先考虑前缀最大值部分。如果考虑按位置插入,由于要记录上升点个数,所以每次都要记录目前末尾的数,状态数就已经四维了,无法通过。于是考虑看起来更有道理的按数值插入。
会发现按数值大小从小到大插入时最大值个数不好统计,应该是需要再加一维状态的,于是考虑从大到小插入。
按数值大小从大到小插入时将数插入到排列最前面,这个数就是前缀最大值,否则就不是;将数插入到任意一个非上升点前面,就能新增一个上升点,否则就无法增加。于是可以三次方 dp 求出每种排列长度,前缀最大值个数,上升点个数对应的排列的方案数。
由于前缀最大值个数和后缀最大值个数相互独立,因此只需要记录每种排列长度和上升点个数所对应的所有前缀最大值个数对应的方案数乘上相应的 \(a_x\) 的和即可,记为 \(f_{i,j}\)。
后缀最大值部分与前缀最大值部分相似,这里不讲,最后求出来 \(g_{i,j}\)。
记排列最大值前面有 \(s1\) 个数,后面后 \(s2\) 个数,前面有 \(x\) 个上升点,后面有 \(y\) 个上升点,答案序列为 \(ans\),那么 \(ans_{s1+s2+1}\) 就是 \(f_{s1,x}\times g_{s2,y}\times \binom{s1+s2}{s1}\times c_{x+1+y}\) 的和。复杂度是四次方的,得优化。
会发现所有有 \(x\) 的项都没有 \(s2\),于是先枚举 \(s1\) 和 \(y\),求出所有 \(f_{s1,x}\times c_{x+1+y}\) 的和,记为 \(h_{s1,y}\),于是 \(ans_{s1+s2+1}\) 就是 \(h_{s1,y}\times g_{s2,y}\times \binom{s1+s2}{s1}\) 的和。复杂度是三次方的,可以通过。
CODE:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n;
ll jc[710],inv[710],jin[710];
ll a[710],b[710],c[710];
ll dp[2][710][710],f[710][710],g[710][710];
ll h[710][710];
ll ans[710];
const ll mod=998244353;
inline ll add(ll x,ll y){if(x+y>=mod) return x+y-mod;return x+y;}
inline ll C(int x,int y){if(x<y||x<0||y<0) return 0;return jc[x]*jin[y]%mod*jin[x-y]%mod;}
int main()
{
scanf("%d",&n);
jc[0]=jc[1]=inv[1]=jin[0]=jin[1]=1;
for(register ll i=2;i<=n;i++) jc[i]=jc[i-1]*i%mod,inv[i]=(mod-mod/i)*inv[mod%i]%mod,jin[i]=jin[i-1]*inv[i]%mod;
for(register int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(register int i=1;i<=n;i++) scanf("%lld",&b[i]);
for(register int i=0;i<n;i++) scanf("%lld",&c[i]);
dp[1][1][0]=1,f[1][0]=add(f[1][0],dp[1][1][0]*a[2]%mod);
for(register int i=2;i<=n;i++)
{
int nw=(i&1);
for(register int j=1;j<=i-1;j++)
for(register int k=0;k<=i-2;k++)
dp[nw][j+1][k+1]=add(dp[nw][j+1][k+1],dp[nw^1][j][k]),
dp[nw][j][k]=add(dp[nw][j][k],1ll*(k+1)*dp[nw^1][j][k]%mod),
dp[nw][j][k+1]=add(dp[nw][j][k+1],1ll*(i-1-k-1)*dp[nw^1][j][k]%mod);
for(register int j=1;j<=i;j++)
for(register int k=0;k<=i-1;k++)
f[i][k]=add(f[i][k],dp[nw][j][k]*a[j+1]%mod);
for(register int j=1;j<=i-1;j++)
for(register int k=0;k<=i-2;k++)
dp[nw^1][j][k]=0;
}
for(register int j=1;j<=n;j++)
for(register int k=0;k<=n-1;k++)
dp[n&1][j][k]=0;
dp[1][1][0]=1,g[1][0]=add(g[1][0],dp[1][1][0]*b[2]%mod);
for(register int i=2;i<=n;i++)
{
int nw=(i&1);
for(register int j=1;j<=i-1;j++)
for(register int k=0;k<=i-2;k++)
dp[nw][j+1][k]=add(dp[nw][j+1][k],dp[nw^1][j][k]),
dp[nw][j][k]=add(dp[nw][j][k],1ll*k*dp[nw^1][j][k]%mod),
dp[nw][j][k+1]=add(dp[nw][j][k+1],1ll*(i-1-k)*dp[nw^1][j][k]%mod);
for(register int j=1;j<=i;j++)
for(register int k=0;k<=i-1;k++)
g[i][k]=add(g[i][k],dp[nw][j][k]*b[j+1]%mod);
for(register int j=1;j<=i-1;j++)
for(register int k=0;k<=i-2;k++)
dp[nw^1][j][k]=0;
}
ans[1]=a[1]*b[1]%mod*c[0]%mod;
for(register int s1=1;s1<=n-1;s1++) for(register int x=0;x<=s1-1;x++) ans[s1+1]=add(ans[s1+1],f[s1][x]*b[1]%mod*c[x+1]%mod);
for(register int s2=1;s2<=n-1;s2++) for(register int y=0;y<=s2-1;y++) ans[s2+1]=add(ans[s2+1],g[s2][y]*a[1]%mod*c[y]%mod);
/*for(register int s1=1;s1<=n-1;s1++)
for(register int s2=1;s1+s2<=n-1;s2++)
for(register int x=0;x<=s1-1;x++)
for(register int y=0;y<=s2-1;y++)
ans[s1+s2+1]=add(ans[s1+s2+1],f[s1][x]*g[s2][y]%mod*C(s1+s2,s1)%mod*c[x+1+y]%mod);*/
for(register int s1=1;s1<=n-1;s1++)
for(register int x=0;x<=s1-1;x++)
for(register int y=0;x+1+y<=n-2;y++)
h[s1][y]=add(h[s1][y],f[s1][x]*c[x+1+y]%mod);
for(register int s1=1;s1<=n-1;s1++)
for(register int s2=1;s1+s2<=n-1;s2++)
for(register int y=0;y<=s2-1;y++)
ans[s1+s2+1]=add(ans[s1+s2+1],h[s1][y]*g[s2][y]%mod*C(s1+s2,s1)%mod);
for(register int i=1;i<=n;i++) printf("%lld ",ans[i]);
puts("");
return 0;
}

浙公网安备 33010602011771号