bzoj 4559 [JLoi2016]成績比較 —— DP+拉格朗日插值
題目:https://www.lydsy.com/JudgeOnline/problem.php?id=4559
看了看拉格朗日插值:http://www.cnblogs.com/ECJTUACM-873284962/p/6833391.html
https://blog.csdn.net/lvzelong2014/article/details/79159346
https://blog.csdn.net/qq_35649707/article/details/78018944
還只會最簡單的那種,正好在這道題裡可以用到;
計算方案數,可以考慮DP,利用那個所有成績都小於 B 的性質,列舉超過 B 的一門課;
設計 f[i][j] 表示當前到了第 i 門課,還剩 j 個人被碾壓(一開始是所有人都被碾壓,然後漸漸突破...);
則 f[i][j] = ∑(j<=t<=n-1) f[i-1][t] * C(n-1-t,rk[i]-1-(t-j)) * C(t,j) * g[i]
其中第一個組合數表示在 n-1-t 個上一次已經不被碾壓的人中選出 rk[i]-1-(t-j) 個作為這次成績高於 B 的人,第二個組合數表示從 t 個上次被碾壓的人中選出 j 個這次仍然被碾壓(也等同與選出 t-j 個人這次成績高於 B );
g[i] 則表示在 i 這門課上的成績分佈情況,則選出的人的成績可以對號入座;
而 g[i] = ∑(1<=j<=lim[i]) j^(n-rk[i]) * (lim[i]-j)^(rk[i]-1),表示若 B 的成績是 j,則有 n-rk[i] 個人的成績在 1~j 中選擇,有 rk[i]-1 個人的成績在 lim[i]-j~lim[i] 中選擇;
可以發現這是個大約 n+1 次的多項式,所以設出幾個點,求出當 x=lim 時的取值即可,這個過程的複雜度是 n^2 的。
程式碼如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const xn=105,mod=1e9+7; int n,m,K,lm[xn],rk[xn],g[xn],c[xn][xn],f[xn][xn],xx[xn],yy[xn];int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return f?ret:-ret; } int pw(ll a,int b) { ll ret=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1)ret=(ret*a)%mod; return ret; } int upt(int x){while(x>=mod)x-=mod; while(x<0)x+=mod; return x;} void init() { for(int i=0;i<=n;i++)c[i][0]=1; for(int i=1;i<=n;i++) for(int j=1;j<=i;j++)c[i][j]=upt(c[i-1][j]+c[i-1][j-1]); } int solve(int lim,int n,int m) { int num=n+m+2,sum=0; for(int i=1;i<=num;i++) xx[i]=i,yy[i]=upt(yy[i-1]+(ll)pw(i,n)*pw(lim-i,m)%mod); for(int i=1;i<=num;i++) { ll s1=1,s2=1; for(int j=1;j<=num;j++) if(i!=j)//!!! s1=s1*(lim-xx[j])%mod,s2=s2*(xx[i]-xx[j])%mod; sum=upt(sum+s1*pw(s2,mod-2)%mod*yy[i]%mod); } return sum; } int main() { n=rd()-1; m=rd(); K=rd(); init();//n-1 for(int i=1;i<=m;i++)lm[i]=rd(); for(int i=1;i<=m;i++)rk[i]=rd(),g[i]=solve(lm[i],n-rk[i]+1,rk[i]-1);//+1 f[0][n]=1;//n for(int i=1;i<=m;i++) for(int j=K;j<=n;j++)//k for(int t=j;t<=n;t++) { if(t-j>rk[i]-1||j>n-rk[i]+1)continue;//+1! f[i][j]=upt(f[i][j]+(ll)f[i-1][t]*c[t][j]%mod*c[n-t][rk[i]-1-t+j]%mod*g[i]%mod); } printf("%d\n",f[m][K]); return 0; }