专栏文章
P12251 [科大国创杯初中组 2025] 抽卡 题解
P12251题解参与者 10已保存评论 9
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 9 条
- 当前快照
- 1 份
- 快照标识符
- @mipfx4bq
- 此快照首次捕获于
- 2025/12/03 11:21 3 个月前
- 此快照最后确认于
- 2025/12/03 11:21 3 个月前
场外 VP,小朋友太惨了。
做法
非常经典地,将 的数视为 ,其余视为 ,这样就将贡献拆掉,问题简化。
与马队和 asdfz 的诸位大佬不同的是,我的朴素 DP 是正着做的(这样似乎更易理解和记答案?)。
设 表示前 个数中合法选出 个 的概率,。
则 ,其中 。
易知对答案的贡献为 。
代码如下:
CPP#include<bits/stdc++.h>
using namespace std;
const int N=505;
const long long mod=1000000007;
long long f[N][2],inv,ans;
int n,m,s,a[N],p[N];
long long qpow(long long x,int y)
{
long long z=1;
while(y)
{
if(y&1)
z=z*x%mod;
x=x*x%mod,y>>=1;
}
return z;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int cur;
cin>>n>>m;
for(int i=1;i<=n;++i)
cin>>a[i],p[i]=a[i]+p[i-1];
for(int t=1;t<=n;s+=a[t],++t)
{
for(int x=s+1;x<=s+a[t];++x)
{
memset(f,0,sizeof f),f[0][0]=1,cur=1;
for(int i=t;i<=n;++i,cur^=1)
{
inv=(p[i]-x+1)*qpow(p[i],mod-2)%mod;
for(int j=0;j<=i;++j)
{
f[j][cur]=f[j][!cur]*(1-inv)%mod;
if(j>=2)
f[j][cur]=(f[j][cur]+f[j-2][!cur]*inv)%mod;
}
f[i][cur]=(f[i][cur]+f[i-1][!cur]*inv)%mod;
}
for(int j=1;j<=n;++j)
ans=(ans+j*f[j][!cur])%mod;
}
}
cout<<(ans+mod)%mod;
return 0;
}
做法
上述做法大约只能用拉插优化到 ,原因在于未能有效利用之前的信息。
要作一个转化,合法相当于后 个数至少取 个,即在每个 处取局部最优值,再拆一下期望,具体见 Purslane 的题解。
下面就与他们的 DP 式子一样了,处理时我是记录下降幂的系数,使用一些有限微积分的技巧求和,即:
。
代码如下:
CPP#include<bits/stdc++.h>
using namespace std;
const int N=505;
const long long mod=1000000007;
long long q[N][2],invv[N],ans;
int n,m,s,a[N],p[N];
struct poly{
long long a[N]; //下降幂
void operator+=(const poly &x)
{
for(int i=0;i<=n;++i)
a[i]=(a[i]+x.a[i])%mod;
}
poly operator*(const long long x)
{
poly y;
for(int i=0;i<=n;++i)
y.a[i]=a[i]*x%mod;
return y;
}
}f[N][2],A;
poly times(const poly &x,const long long a,const long long b)
{
poly y;
y.a[0]=x.a[0]*b%mod*a%mod;
for(int i=1;i<=n;++i)
y.a[i]=(x.a[i-1]+x.a[i]*(b+i))%mod*a%mod;
return y;
}
long long qpow(long long x,int y)
{
long long z=1;
while(y)
{
if(y&1)
z=z*x%mod;
x=x*x%mod,y>>=1;
}
return z;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int cur=1;
long long inv,u=0,v=0;
cin>>n>>m;
invv[0]=1;
for(int i=1;i<=n;++i)
cin>>a[i],p[i]=a[i]+p[i-1],invv[i]=qpow(i+1,mod-2);
f[0][0].a[0]=1;
for(int i=n;i>=1;--i,cur^=1)
{
memset(A.a,0,sizeof A.a),inv=qpow(p[i],mod-2)%mod,u+=2,v=(v-2*inv)%mod;
for(int j=n-i+1;j>=0;--j)
{
f[j][cur]=times(f[j+1][!cur],inv,0);
if(j==0)
f[j][cur]+=times(f[j][!cur],inv,0);
else
f[j][cur]+=times(f[j-1][!cur],-inv,-p[i]);
}
for(int j=0;j<=n;++j)
A+=f[j][cur]*(-max(j-i+1,0));
A.a[0]+=u,A.a[1]+=v;
q[0][0]=q[0][1]=1;
for(int j=1;j<=n+1;++j)
q[j][0]=q[j-1][0]*(p[i]+1-j)%mod,q[j][1]=q[j-1][1]*(p[i-1]+1-j)%mod;
for(int j=0;j<=n;++j)
ans+=A.a[j]*invv[j]%mod*(q[j+1][0]-q[j+1][1])%mod;
ans%=mod;
}
cout<<(ans+mod)%mod;
return 0;
}
所以什么时候我能给科大国创出题啊。
本题解与笔者的代码在伟大的 Purslane 的指导下完成,让我们一起拜谢马队,祝他在绍兴 Au。
相关推荐
评论
共 9 条评论,欢迎与作者交流。
正在加载评论...