P14031 【MX-X20-T5】「FAOI-R7」连接时光 II
shiziyu111 · · 题解
维护数字之间的相对顺序,考虑从左往右依次插入数字。
不难发现有两个性质:
- 如果将数放在当前的最高位,那么就会造成最大值和其他数不连通,我们姑且叫他缺口。(可以有多个缺口)
- 如果我们将数插入在
i 与i+1 之间,那么所有> i 的数字都会联通,我们称为补上缺口。
继续观察,我们发现当前只有最小的缺口是重要的,因为最小的缺口一定不早于更大的缺口被补上。
那么我们的状态设计就变得简单了:设
那么再从在最上面,最小缺口上还是最小缺口下方数等分类讨论即可,代码如下,其中
f[1][0]=1*((s[1]=='1')+1);
for(int i=2;i<=n;i++)
{
f[i][i-1]+=f[i-1][0];//在最上面放数
f[i][i-1]%=mod;
for(int j=1;j<=i-1;j++)
{
f[i][0]+=f[i-1][0]*z[i][i-j]%mod;//在除了最上面的地方放数
f[i][0]%=mod;
}
for(int j=1;j<i;j++)//最小缺口
{
for(int k=1;k<=i;k++)//插入位置
{
if(j<k)
{
f[i][j]+=f[i-1][j]*z[i][i-k]%mod;//在缺口上方放数
f[i][j]%=mod;
}
else
{
f[i][0]+=f[i-1][j]*z[i][i-k]%mod;//在缺口下方放数
f[i][0]%=mod;
}
}
}
if(s[i]=='1')
{
f[i][0]*=2;//这里要注意,当 s_i 取 1 时没有缺口符合条件,所以要*2
f[i][0]%=mod;
}
}
不难发现这个是可以使用前缀和优化的,补上即可。
最后代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5005,mod=998244353;
int a[N];
char s[N];
int n;
int f[N][N],z[N][N],sum[N];
void solve()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
scanf("%s",s+1);
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
f[i][j]=0;
}
}
for(int i=1;i<=n;i++)
{
z[i][0]=1;
for(int j=1;j<=n;j++)
{
z[i][j]=z[i][j-1]*a[i]%mod;
}
}
f[1][0]=((s[1]=='1')+1);
for(int i=2;i<=n;i++)
{
f[i][i-1]+=f[i-1][0];
f[i][i-1]%=mod;
for(int j=1;j<=i-1;j++)
{
f[i][0]+=f[i-1][0]*z[i][i-j]%mod;
f[i][0]%=mod;
}
for(int j=1;j<=n;j++)
{
sum[j]=sum[j-1]+z[i][i-j];
sum[j]%=mod;
}
for(int j=1;j<i;j++)
{
f[i][j]+=f[i-1][j]*(sum[i]-sum[j])%mod;
f[i][j]%=mod;
f[i][0]+=sum[j]*f[i-1][j]%mod;
f[i][0]%=mod;
}
if(s[i]=='1')
{
f[i][0]*=2;
f[i][0]%=mod;
}
}
int ans=0;
for(int i=0;i<=n;i++)
{
ans+=f[n][i];
ans%=mod;
}
ans=(ans%mod+mod)%mod;
printf("%lld\n",ans);
}
signed main()
{
int T;
scanf("%lld",&T);
while(T--)
{
solve();
}
return 0;
}