bzoj4559[JLoi2016]成绩比较 容斥+拉格朗日插值法

4559: [JLoi2016]成绩比较

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 261  Solved: 165
[Submit][Status][Discuss]

Description

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神那么厉害,你只需要计算出情况数模1

0^9+7的余数就可以了。

Input

第一行包含三个正整数N,M,K,分别表示G系的同学数量(包括B神),必修课的数量和被B神碾压的同学数量。第二

行包含M个正整数,依次表示每门课的最高分Ui。第三行包含M个正整数,依次表示B神在每门课上的排名Ri。保证1

≤Ri≤N。数据保证至少有1种情况使得B神说的话成立。N<=100,M<=100,Ui<=10^9

Output

仅一行一个正整数,表示满足条件的情况数模10^9+7的余数。

Sample Input

3 2 1
2 2
1 2

Sample Output

10

HINT
Source

容斥+拉格朗日插值法

 

讲一个性质:

对于n阶等差数列 (如 1^5,2^5,3^5,4^5….被称为5阶等差)

它们的前n项和可以用一个最高次为n+1次的多项式表示

它们的通项公式可以用一个最高次为n次的多项式表示

因此,我们可以用拉格朗日插值法来优化一些多项式计算

 


(其实我不是很懂)
推荐blog
http://www.cnblogs.com/zj75211/p/8029303.html

#include<cstdio> 
#include<cstring>
#include<iostream>
#define MAXN 105
#define _ %mod
#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
const int mod=1000000007;
int dp[MAXN],U[MAXN],R[MAXN],C[MAXN][MAXN],Y[MAXN],inv[MAXN];
int N,M,K,ANS;
int pow(int a,int b){
    int now=1;
    while(b){
        if(b&1) now=(1ll*now*a)_;
        a=(1ll*a*a)_; b>>=1;
    }
    return now;
}
int Lagrange(int u,int r){
    static int lpi[MAXN],rpi[MAXN],p[MAXN],ans,tmp;
    lpi[0]=1; rpi[N+2]=1; ans=0;
    for(int i=1;i<=N+1;i++){
        p[i]=(1ll*p[i-1]+1ll*pow(i,N-r)*pow(u-i,r-1)_)_;
        if(i==u) return p[i];
    }
    for(int i=1;i<=N+1;i++) lpi[i]=1ll*lpi[i-1]*(u-i)_;
    for(int i=N+1;i>=1;i--) rpi[i]=1ll*rpi[i+1]*(u-i)_;
    for(int i=1;tmp=1,i<=N+1;i++){
        tmp=1ll*tmp*lpi[i-1]_*rpi[i+1]_*inv[i-1]_*inv[N+1-i]_*p[i]_;
        tmp=(1ll*tmp*((N+1-i)&1?-1:1)+mod)_;
        ans=(1ll*ans+tmp)_;
    }
    return ans;
}
int main()
{
    scanf("%d%d%d",&N,&M,&K);
    inv[0]=1; inv[1]=1; 
    for(int i=2;i<=N+1;i++) inv[i]=((-1ll*(mod/i)*inv[mod%i])_+mod)_;
    for(int i=1;i<=N+1;i++) inv[i]=1ll*inv[i]*inv[i-1]_;
    for(int i=1;i<=M;i++) scanf("%d",&U[i]);
    for(int i=1;i<=M;i++) scanf("%d",&R[i]);
    for(int i=0;i<=N;i++){
        C[i][0]=1;
        for(int j=1;j<=i;j++)
            C[i][j]=(1ll*C[i-1][j-1]+C[i-1][j])_;
    }
    for(int i=1;i<=M;i++) Y[i]=Lagrange(U[i],R[i]);
    for(int i=N-1;i>=K;i--)
    {
        dp[i]=C[N-1][i];
        for(int j=1;j<=M;j++)    dp[i]=1ll*dp[i]*C[N-i-1][N-R[j]-i]_;
        ANS=(1ll*ANS+(((i^K)&1)?-1:1)*1ll*dp[i]*C[i][K]_+mod)_;
    }
    for(int i=1;i<=M;i++) ANS=1ll*ANS*Y[i]_;
    printf("%d",(ANS+mod)_);
    return 0;
}

转载于:https://www.cnblogs.com/wsy01/p/8031918.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值