您的位置:首页 > 其它

bzoj4559 成绩比较【动态规划+拉格朗日插值法】

2017-12-12 08:39 309 查看

解题思路:

我们设f[i][j]表示前i门课,共有j个人没有被碾压的方案数,那么答案即为f[m][n−1−k]

假设从f[i−1][w]转移到f[i][j](j≥w)

没被碾压的人数本应增加Ri−1人,但其中有一部分可能之前就没被碾压了。

则有t1=j−w个人是新增的人数,要从n−1−w个人中选,方案数为Ct1n−1−w

还有t2=Ri−1−t1个人是本就没被碾压的,要从w个人中选,方案数为Ct2w

例如w=100,Ri−1=5,j=102,则t1=2,t2=3

考虑第i门课的得分方案,若B神得了x分,则有Ri−1个人分数大于他,n−Ri个人分数小等于他,所以总方案数为

g(i)=∑x=1Ui(Ui−x)r−1xn−r

由于Ui很大,无法直接算,但注意到它是一个关于Ui的次数小等于n次的多项式,所以可以用拉格朗日插值法插U=1,2……n+1得到n+1个点进而算出g(i)的值。

由于Ri不同,所以对于每门课都要求一遍。

至此得到总的dp方程:

f[i][j]=g(i)∑w=0jCt1n−1−wCt2wf[i−1][w]

时间复杂度为O(n3)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define ll long long
using namespace std;

int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}

const int N=105,p=1e9+7;
int n,m,k;
int U
,R
,c

,f

;
int g
,inv
;

int ksm(int x,int y)
{
x%=p;int res=1;
for(;y;y>>=1,x=1ll*x*x%p)
if(y&1)res=1ll*res*x%p;
return res;
}

int Inter(int u,int r)
{
memset(g,0,sizeof(g));
for(int x=1;x<=n+1;x++)
{
for(int i=1;i<=x;i++)
g[x]=(g[x]+1ll*ksm(x-i,r-1)*ksm(i,n-r)%p)%p;
if(u==x)return g[x];
}
for(int i=1;i<=n+1;i++)
{
inv[i]=1;
for(int j=1;j<=n+1;j++)
if(i!=j)inv[i]=(1ll*inv[i]*(i-j)%p+p)%p;
inv[i]=ksm(inv[i],p-2);
}
int res=0;
for(int i=1;i<=n+1;i++)
{
int tmp=1ll*inv[i]*g[i]%p;
for(int j=1;j<=n+1;j++)
if(i!=j)tmp=1ll*tmp*(u-j)%p;
res=(res+tmp)%p;
}
return res;
}

int C(int i,int j)
{
if(i<0||j<0||j>i)return 0;
return c[i][j];
}

int main()
{
//freopen("lx.in","r",stdin);
//freopen("lx.out","w",stdout);
n=getint(),m=getint(),k=getint();
for(int i=1;i<=m;i++)U[i]=getint();
for(int i=1;i<=m;i++)R[i]=getint();
c[0][0]=1;
for(int i=1;i<=n;i++)
{
c[i][0]=1;
for(int j=1;j<=n;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
}
f[0][0]=1;
for(int i=1;i<=m;i++)
{
int tmp=Inter(U[i],R[i]);
for(int j=0;j<=n;j++)
{
for(int w=0;w<=j;w++)
f[i][j]=(f[i][j]+1ll*f[i-1][w]*C(n-1-w,j-w)%p*C(w,R[i]-1-(j-w))%p*tmp%p)%p;
}
}
cout<<f[m][n-1-k]<<'\n';
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: