贡献延后延献贡
如何在被贡献延后控制大脑的情况下做出此题?
先将商品按价格从小到大排序。
首先观察到,可以记录没买上的顾客的数量,求一个人没买上时的概率也是容易的,只需要知道此时价格最小的商品即可。
于是可以设计一个状态 \(dp_{i,j,k}\) 表示第 \(i\) 个顾客买完了,此时价格最小的商品为第 \(j\) 个商品,没买商品顾客数量为 \(k\)。
但是会发现买上商品的顾客的概率不好计算,此时可以考虑贡献延后,如果这个顾客买的商品不是第 \(j\) 个商品,就扔到后面去计算贡献,如果刚好买的是第 \(j\) 个商品,就枚举买完之后的价格最小的商品时第几个,这里记为 \(l\),然后对于第 \(j+1\) 个到第 \(l-1\) 个商品的购买情况就用前面存下的延后贡献的顾客去计算。
那么要如何去计算第 \(j+1\) 到第 \(l-1\) 个商品的购买情况的概率呢?可以考虑区间 dp。设状态为 \(f_{l,r}\),表示第 \(l\) 个到第 \(r\) 个商品全被买完的概率。状态转移考虑枚举这个区间最后一个买的商品,记为 \(k\),有状态转移方程 \(f_{l,r}=\sum f_{l,k-1}\times f_{k+1,r}\times \frac{a_{r+1}-a_k}{c}\times \binom{r-l}{k-l}\)。
于是此时 \(dp_{i,j,k}\) 的状态转移方程也弄出来了。记目前延后贡献的顾客数量为 \(p\),则 \(p=i-k-(j-1)\)。此时有状态转移方程:
其中第一个表示将这个顾客的贡献延后计算时的转移,第二个表示这个顾客买不到商品时的转移,第三个表示这个顾客买到了第 \(j\) 个商品时的转移。
但是统计答案的时候会发现对于一些状态,可能会存在一些贡献延后的顾客还没计算他们的贡献,此时还要再写一个 dp 计算他们的贡献。设计状态 \(g_{i,j,l}\) 表示此时价格最小的商品是 \(i\),已经考虑到第 \(j\) 个商品且该商品没被买,已经有 \(l\) 个顾客买了商品。每次转移考虑枚举上一个没被买的商品 \(k\) 以及这个时候的 \(l\),于是可以写出状态转移方程 \(g_{i,j,l+(j-k-1)}\gets g_{i,j,l+(j-k-l)}+g_{i,k,l}\times f_{k+1,j-1}\times \binom{l+(j-k-1)}{j-k-1}\)。
那么枚举卖出的商品数 \(k\),记此时的答案为 \(ans_k\),枚举此时价格最小的商品 \(j\),则有 \(ans_k=\sum dp_{n,j,n-k}\times g_{j,n+1,k-(j-1)}\)。
具体细节看代码。
CODE:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t;
int n;ll c,invc;
ll jc[110],inv[110],jin[110];
ll a[110];
ll f[110][110],g[110][110][110];
ll dp[110][110][110];
const ll mod=998244353;
inline ll add(ll x,ll y){if(x+y>=mod) return x+y-mod;return x+y;}
inline ll qpow(ll x,ll y){ll sum=1;while(y){if(y&1) sum*=x,sum%=mod;x*=x,x%=mod,y>>=1;}return sum;}
inline ll C(int x,int y){if(x<0||y<0||x<y) return 0;return jc[x]*jin[y]%mod*jin[x-y]%mod;}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%lld",&n,&c),invc=qpow(c,mod-2);
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]);
sort(a+1,a+n+1),a[n+1]=c+1;
for(register int i=1;i<=n+1;i++) f[i][i-1]=1;
for(register int len=1;len<=n;len++)
for(register int l=1;l+len-1<=n;l++)
{
int r=l+len-1;
for(register int k=l;k<=r;k++) f[l][r]=add(f[l][r],f[l][k-1]*f[k+1][r]%mod*(a[r+1]-a[k])%mod*invc%mod*C(len-1,k-l)%mod);
}
dp[0][1][0]=1;
for(register int i=1;i<=n;i++)
for(register int j=1;j<=n;j++)
for(register int k=0;k<=n;k++)
{
int p=(i-1)-k-(j-1);
if(p<0) continue;
dp[i][j][k]=add(dp[i][j][k],dp[i-1][j][k]);
if(k+1<=n) dp[i][j][k+1]=add(dp[i][j][k+1],(a[j]-1)*invc%mod*dp[i-1][j][k]%mod);
for(register int l=j+1;l<=n+1;l++)
{
if(l-j-1>p) break;
dp[i][l][k]=add(dp[i][l][k],dp[i-1][j][k]*f[j+1][l-1]%mod*(a[l]-a[j])%mod*invc%mod*C(p,l-j-1)%mod);
}
}
for(register int i=1;i<=n+1;i++)
{
g[i][i][0]=1;
for(register int j=i+1;j<=n+1;j++)
for(register int k=i;k<j;k++)
for(register int l=0;l<=n;l++)
if(l+(j-k-1)<=n)
g[i][j][l+(j-k-1)]=add(g[i][j][l+(j-k-1)],g[i][k][l]*f[k+1][j-1]%mod*C(l+(j-k-1),j-k-1)%mod);
}
for(register int k=n;k>=0;k--)
{
ll ans=0;
for(register int j=1;j<=n+1;j++)
{
int p=n-k-(j-1);
if(p>=0&&p<=n) ans=add(ans,dp[n][j][k]*g[j][n+1][p]%mod);
}
printf("%lld ",ans);
}
puts("");
for(register int i=0;i<=n+1;i++)
for(register int j=0;j<=n+1;j++)
for(register int k=0;k<=n+1;k++)
dp[i][j][k]=0,g[i][j][k]=0;
for(register int i=0;i<=n+1;i++)
for(register int j=0;j<=n+1;j++)
f[i][j]=0;
}
return 0;
}

浙公网安备 33010602011771号