题解:P12251 [科大国创杯初中组 2025] 抽卡【暂无数据】
\text{Solution}
先分析一下如果牌堆确定了应该怎么做最优:
倒着处理牌堆,每次将两张卡牌的价值加入堆中并再弹出堆中最大值,
52 分 \mathcal{O}(n^2m) 做法
先分析一下
所有牌的值之和很好求。
对于一种方案堆中剩的所有值之和我们考虑拆贡献:
比较明显
现在如果我们得到了
所以
因此从后往前暴力算
设
即:
最后我们用斯特林数或拉插求自然数幂和即可。
斯特林数求法:
数据出来再放代码。
#include<bits/stdc++.h>
using namespace std;
const int N=505,mod=1e9+7;
#define vr vector<int>
int n,m,a[N],s=1,ans,sss,mi[N],inv[N],st[N][N],ss[N][N];vr f[N][N];
inline int pw(int x,int y) {
int z=1;
for(;y;y>>=1,x=x*1ll*x%mod) if(y&1) z=z*1ll*x%mod;
return z;
}
inline void ad(int &u,int v) {u+=v;(u>=mod&&(u-=mod));}
inline vr add2(vr x,vr y) {
vr z(max(x.size(),y.size()),0);
for(int i=0;i<x.size();++i) ad(z[i],x[i]);
for(int i=0;i<y.size();++i) ad(z[i],y[i]);
return z;
}
inline vr mul(vr x,vr y) {
vr z(x.size()+y.size()-1,0);
for(int i=0;i<x.size();++i) for(int j=0;j<y.size();++j) ad(z[i+j],x[i]*1ll*y[j]%mod);
return z;
}
inline int calc(int x) {return x*1ll*(x+1)/2ll%mod;}
vr tp;
inline vr make(int x,int y) {vr().swap(tp);tp.emplace_back(y);tp.emplace_back(x);return tp;}
inline vr make2(int x) {vr().swap(tp);tp.emplace_back(x);return tp;}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",a+i),a[i]+=a[i-1],s=s*1ll*a[i]%mod;sss=s;
for(int i=1;i*2<=n;++i) swap(a[i],a[n+1-i]);
for(int i=1;i<=n;++i) ans=(ans+calc(a[i])*1ll*s%mod*pw(a[i],mod-2))%mod;
st[0][0]=1;ans=ans*2%mod;
for(int i=1;i<=n;++i) for(int j=1;j<=i;++j) st[i][j]=(j*1ll*st[i-1][j]+st[i-1][j-1])%mod;
for(int i=1;i<=n+2;++i) inv[i]=pw(i,mod-2);
for(int i=1;i<=n;++i) {
mi[0]=1;
for(int j=1;j<=n+1;++j) mi[j]=mi[j-1]*1ll*(a[i]+2-j)%mod;
for(int j=0;j<=n;++j) for(int k=0;k<=j;++k) ss[i][j]=(ss[i][j]+st[j][k]*1ll*inv[k+1]%mod*mi[k+1])%mod;
}
f[0][0].emplace_back(1);ss[n+1][0]=1;
for(int i=1;i<=n;++i) {
for(int j=0;j<i;++j) {
f[i][j+1]=add2(f[i][j+1],mul(make(mod-1,a[i]+1),f[i-1][j]));
f[i][max(j-1,0)]=add2(f[i][max(j-1,0)],mul(make(1,mod-1),f[i-1][j]));
}
vr g;g.clear();
for(int j=n-i+1;j<=n;++j) g=add2(g,mul(make2(j-n+i),f[i][j]));
s=s*1ll*pw(a[i],mod-2)%mod;
for(int j=0;j<(int)g.size();++j) ans=(ans-g[j]*1ll*s%mod*(ss[i][j]-ss[i+1][j]))%mod;
}
if(ans<0) ans+=mod;ans=ans*1ll*pw(sss,mod-2)%mod;
printf("%d\n",ans);
return 0;
}