1. 程式人生 > >bzoj4559 [JLOI2016]成績比較 拉格朗日插值

bzoj4559 [JLOI2016]成績比較 拉格朗日插值

\n printf 防止 正整數 ... 所有 表示 sum getchar

題目描述

G系共有n位同學,M門必修課。這N位同學的編號為0到N-1的整數,其中B神的編號為0號。這M門必修課編號為0到M-1的整數。一位同學在必修課上可以獲得的分數是1到Ui中的一個整數。

如果在每門課上A獲得的成績均小於等於B獲得的成績,則稱A被B碾壓。在B神的說法中,G系共有K位同學被他碾壓(不包括他自己),而其他N-K-1位同學則沒有被他碾壓。D神查到了B神每門必修課的排名。

這裏的排名是指:如果B神某門課的排名為R,則表示有且僅有R-1位同學這門課的分數大於B神的分數,有且僅有N-R位同學這門課的分數小於等於B神(不包括他自己)。

我們需要求出全系所有同學每門必修課得分的情況數,使其既能滿足B神的說法,也能符合D神查到的排名。這裏兩種情況不同當且僅當有任意一位同學在任意一門課上獲得的分數不同。

你不需要像D神那麽厲害,你只需要計算出情況數模10^9+7的余數就可以了。

輸入格式:

第一行包含三個正整數N,M,K,分別表示G系的同學數量(包括B神),必修課的數量和被B神碾壓的同學數量。

第二行包含M個正整數,依次表示每門課的最高分Ui。

第三行包含M個正整數,依次表示B神在每門課上的排名Ri。保證1<=Ri<=N。

數據保證至少有1種情況使得B神說的話成立。

輸出格式:

僅一行一個正整數,表示滿足條件的情況數模10^9+7的余數。


\(f[i][j]中i表示處理到第i個科目,j表示到第i個科目有j個人被B神碾壓\)

\(轉移條件為\)
\[ f[i][j]=\sum_{k=j}^{n-1}{f[i-1][k]*C_{k}^{k-j}*C_{n-1-k}^{rank[i]-1-k+j}*\sum_{p=1}^{U_i}p^{n-rank[i]}(U_i-p)^{rank[i]-1}} \]


對公式的理解可以參考https://www.luogu.org/blog/winxp/solution-p3270

然後後面的\(g[i]=\sum_{p=1}^{U_i}p^{n-rank[i]}(U_i-p)^{rank[i]-1}\)就是關於\(U_i\)\(n\)次式,利用拉格朗日插值法求值即可。
具體實現:記錄每次\(rank[i]\)情況下的\(g[i] (i\in 1,2,...,n)\)的值,然後利用拉格朗日插值公式求解\(g[U_i]\)的值

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=105;
const int mod=1e9+7;
int read(){
    int x=0,ff=1;char ch=getchar();
    while(!isdigit(ch)){if(ch==‘-‘)ff=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-‘0‘;ch=getchar();}
    return x*ff;
}
int C[N][N];
int f[N][N];
int g[N];
int n,m,K;
int mx[N],rnk[N];
void init(){
    C[0][0]=1;
    for(int i=1;i<N;i++){//千萬不要等於N,爆數組就錯了……調了半天qwq
        C[0][i]=1;
        for(int j=1;j<=i;j++)
            C[j][i]=1LL*(C[j-1][i-1]+C[j][i-1])%mod;
    }
}
int pow(int a,int b){
    int res=1;
    while(b){
        if(b&1)res=1LL*res*a%mod;
        a=1LL*a*a%mod;
        b/=2;
    }
    return res;
}
int cal(int U,int R){
    memset(g,0,sizeof(g));
    for(int i=1;i<N;i++) 
        for(int j=1;j<=i;j++)
            g[i]=1LL*(g[i]+1LL*pow(j,n-R)*pow(i-j,R-1)%mod)%mod;
    int res=0;
    for(int i=1;i<N;i++){
        int a=g[i],b=1;
        for(int j=1;j<N;j++){
            if(i==j)continue;
            a=1LL*a*(U-j)%mod;
            a=(a+mod)%mod;//註意防止為負
            b=1LL*b*(i-j)%mod;
            b=(b+mod)%mod;
        }
        res=1LL*(res+1LL*a*pow(b,mod-2)%mod)%mod;
    }
    return res;
}
int main()
{
    // freopen("4559.in","r",stdin);
    // freopen("4559.out","w",stdout);
    n=read();m=read();K=read();
    init();
    f[0][n-1]=1;
    for(int i=1;i<=m;i++)mx[i]=read();
    for(int i=1;i<=m;i++)rnk[i]=read();
    for(int i=1;i<=m;i++){
        int d=cal(mx[i],rnk[i]);
        for(int j=K;j<=n-1;j++)
            for(int k=j;k<=n-1;k++){
                if(k-j>rnk[i]-1)continue;
                int tmp=1LL*C[k-j][k]*C[rnk[i]-1-k+j][n-1-k]%mod;
                f[i][j]=1LL*(f[i][j]+1LL*f[i-1][k]*tmp%mod*1LL*d%mod)%mod;
            }
    }
    printf("%d\n",f[m][K]);
    return 0;
}

bzoj4559 [JLOI2016]成績比較 拉格朗日插值