1. 程式人生 > >BZOJ4599[JLoi2016&LNoi2016]成績比較(dp+拉格朗日插值)

BZOJ4599[JLoi2016&LNoi2016]成績比較(dp+拉格朗日插值)

calc 需要 names res esp 轉移 等於 sin 必須

這個題我們首先可以dp,f[i][j]表示前i個科目恰好碾壓了j個人的方案數,然後進行轉移。我們先不考慮每個人的分數,先只關心和B的相對大小關系。我們設R[i]為第i科比B分數少的人數,則有f[i][j]=sum f[i-1][k]*C(k,j)*C(n-1-k,R[i]-j) (k>=j) 怎麽解釋呢,首先前i-1科有k個人已經被碾壓,k肯定大於等於j,然後考慮當前這一科有j個人被碾壓,那麽就需要從k個人中選出j個來即C(k,j),然後從剩下的有R[i]-j個人比B考的少,這些人必須是之前i-1科裏就沒有被碾壓的人,所以再乘上一個C(n-1-j,R[i]-k),到此我們dp完了,可是我們還需要算上每個人的分數,這個東西很明顯可以乘上我們的f[m][k]得到答案。 這些分數的方案數是什麽呢?對於第i科成績,有n-R[i]-1個人比B考的多,有R[i]個人比B少,因為我們之前考慮了相對大小關系,這裏直接很明顯的算就行了就是技術分享圖片

然後我們算n次sigam即可,把他們乘在一起。但是由於Ui是1e9級別的,直接暴力算肯定會超時,我們可以用拉格朗日插值來算。

很明顯這是一個n次的多項式,所以我們利用插值就可以算出答案了。 —— by VANE

#include<iostream>
#include<cstdio>
#include<cstring>
#define MN 105
#define mod 1000000007
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char
ch = getchar(); while(ch < 0 || ch > 9){ if(ch == -) f = -1; ch = getchar();} while(ch >= 0 && ch <= 9){x = x * 10 + ch - 0;ch = getchar();} return x * f; } int inv[MN+5],Inv[MN+5],p[MN+5],U[MN+5],R[MN+5],n,m,K,f[MN+5][MN+5]; inline int pow(int x,int
k) { int sum=1; for(;k;k>>=1,x=1LL*x*x%mod) if(k&1) sum=1LL*sum*x%mod; return sum; } inline int C(int n,int m){return 1LL*p[n]*Inv[m]%mod*Inv[n-m]%mod;} inline void Re(int&x,int y){x+=y;x>=mod?x-=mod:0;} inline int Calc(int m,int rk) { if(m<=n+1) { int res=0; for(int i=1;i<=m;++i) res=(res+1LL*pow(i,rk)*pow(m-i,n-rk-1))%mod; return res; } int sum=1,res=0,Div=1,S=0; for(int i=1;i<=n+1;++i) sum=1LL*sum*(m-i+mod)%mod; for(int i=2;i<=n+1;++i) Div=1LL*Div*(1-i+mod)%mod; for(int i=1;i<=n+1;++i) { int t=1LL*sum*pow(m-i+mod,mod-2)%mod; S=(S+1LL*pow(i,rk)%mod*pow(m-i,n-rk-1))%mod; t=1LL*t*S%mod; res=(res+1LL*t*pow(Div,mod-2))%mod; Div=1LL*Div*pow(mod-(n-i+1),mod-2)%mod*i%mod; } return res; } int main() { n=read();m=read();K=read(); for(int i=1;i<=m;++i) U[i]=read(); for(int i=1;i<=m;++i) R[i]=n-read(); inv[0]=inv[1]=p[0]=p[1]=Inv[0]=1; for(int i=2;i<=MN;++i) p[i]=1LL*p[i-1]*i%mod, inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod; for(int i=1;i<=MN;++i) Inv[i]=1LL*Inv[i-1]*inv[i]%mod; f[0][n-1]=1; R[0]=n-1; for(int i=1;i<=m;++i) for(int j=0;j<=R[i];++j) for(int k=j;k<=R[i-1];++k) if(n-1-k>=R[i]-j) Re(f[i][j],1ll*f[i-1][k]*C(k,j)%mod*C(n-1-k,R[i]-j)%mod); int ans=f[m][K]; for(int i=1;i<=m;++i) ans=1LL*ans*Calc(U[i],R[i])%mod; cout<<ans<<endl; return 0; }

BZOJ4599[JLoi2016&LNoi2016]成績比較(dp+拉格朗日插值)