题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=4559

题解:

计数dp,拉格朗日插值法。
真的是神题啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。


先来看看 dp定义:(由于每门课程的分数分布方案是独立的,所以先不管每科实际分数大小所贡献的方案数。)
dp[i][j]:表示前 i门课程,只考虑分数相对大小关系时,B神碾压了 j个人的方法数
也就是说上面的dp定义只是表示一个相对关系的方案数(即不考虑实际分数大小)。
那么dp的转移:
dp[i][k] += dp[i-1][j]*C(j,k)*C(N-j,N-R[i]-k)
含义呢:就是从在前i-1门课程中被碾压的 j 名同学中选出 k 个来继续碾压(即让他们该科分数任比B神低);同时在没有被碾压的 N-j个同学中选出N-R[i]-k个来使得他们的该科分数任比B神低。(使B神排在该门课程的第 R[i]名)

如果设 Y[i]表示第i门课程且B神排在第R[i]名时的分数分布方案数。

最后的答案就是 dp[N][k]*Y[1]*Y[2]*Y[3]*...*Y[M]

但是求这个 Y[i]就有点麻烦了,里面的 U[i]很大呀,根本不能跑循环去求和。
然后就要用到插值法了。 (不懂插值法?快点击这里,简单入门一波,很快的 ~)
再明确一下当前的目标: 某一门课程,总分为 U,B神排名为 R,求分数分布方案数。
令函数


则答案即为    P[U]。
不难发现,求和式的每一项的都不超过 XN-1,同时又最多只有 X项相加,
所以 P[X] 是最高次项的次数不超过 N 的多项式。
那么求出这个多项式,就只需要 N+1个点,就用插值法求出该函数了。
所以O(N2*logN(快速幂的log))的复杂度暴力求出前 N+1个点的函数值
(X1=1,P[1]) , (X2=2,P[2]) ...... (XN+1=N+1,P[N+1])
然后插值法得到函数 P[X]。
然后再 O(N2)的复杂度求出 X=U 时的函数值 P[U],即可得到当前科目的分数分布方案:Y[当前科目]。
又因为有 M 门科目,所以求 Y[]数组的总复杂度为 O(N3*logN)
erge的插值法代码:

ll Lagrange(ll U,ll R)
{
static ll p[MAXN];
for(int i=1;i<=n+1;i++){
p[i]=0;
for(int j=1;j<=i;j++)
p[i]=(p[i]+Pow(j,n-R)*Pow(U-j,R-1))%Mod;
}
ll ret=0;
for(int i=1;i<=n+1;i++){
ll mul=1;
for(int j=1;j<=n+1;j++)if(j!=i)
mul=mul*(U-j+Mod)%Mod*inv[i-j+500]%Mod;
mul=mul*p[i]%Mod;
ret=(ret+mul)%Mod;
}
return (ret+Mod)%Mod;
}

复杂度有点高,但是可以AC。以下是优化。


这里就有一个比较绕的技巧。
因为对于 P[X]来说,不同的 X 取值既影响求和项的个数,又影响每一项的计算参数:


所以我们暴力求前N+1个点的函数值时不得不花费 O(N^2)的代价。
但注意到,我们这么求 P[X]函数是不是太亏了:花费这么高的复杂度却只需要用到 P[U]这一函数值。
所以优化如下,把 P[X] -> P'[X]

注意到了变化么?

是的,我们把求和式的每一项里面的那个自变量 X 改为了一个常数 U。
这样就可以 O(N) 求出前 N+1个点的函数值 P'[X]:
令:
那么 

显然是个前缀的形式,即 P'[X]=P'[X-1]+F[i]。(之前的 P[X]可没有这个性质哦)
求出了 P'函数的 N+1个点后,就可以插值法求出 P'[X],并得到 P'[U]的值,返回答案就好了。
(在此,插值法也可以通过一个预处理前后缀积来做到 O(N)计算某一个函数值,这个就自己去看代码的实现了)


等等,不感觉有点不对么?
P'函数有点不对呀,本来 P 函数还是好好的,结果把里面的自变量改变成了常数,怎么P'还是对的?
的确, P'函数几乎是错的。几乎对于所有的自变量 X,P[X]!=P'[X]!
但是有一个 X'是满足 P[X']=P'[X'],那个 X'=U
也就是说 P[U]=P'[U], 这个P'函数只能在 X=U 时得到正确答案。
为什么呢,即我们把 P函数里面的每一项里的自变量 X变成了常数 U 后,
对于 X 取值不为 U时,显然 P[X]!=P'[X]
可是当 X=U 时,就可以把那个已经看成常数U了的自变量X又看回为自变量,即两个函数此时的取值相同了。
换句话说我们构造了另外一个函数P',使得其函数图像与函数P的图像在 X=U时有交点。(我们牺牲了几乎所有的正确性,只保留了最有用的那部分,以此来提高效率。
吼吼,所以就这样了,求 Y[ ]的复杂度变为了O(N2*logN);
实现看看代码啦。

复杂度 O(N3)(DP部分)+O(N2*logN)(插值法部分)

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 150
#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][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){
{
/* f(i) = ((i)^(N-R) * (U-i)^(R-1)) //级别最大项为 u^(N-1)
p(x) = ∑|i=1,i<=x| ((i)^(R-N) * (U-i)^(R-1))
= ∑|i=1,i<=x| f(i)
p(x) 级别最大项为 u*u^(N-1)=u^N,所以拉格朗日插值法求其多项式函数需要 N+1 个点
返回的答案为 p(u)。*/
}
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]);
dp[0][N-1]=1;
for(int i=1;i<=M;i++)
for(int j=0;j<=N-1;j++)
for(int k=0;k<=min(j,N-R[i]);k++) if(N-1-j>=N-R[i]-k)
dp[i][k]=(1ll*dp[i][k]+1ll*dp[i-1][j]*C[j][k]_*C[N-1-j][N-R[i]-k]_)_;
ANS=dp[M][K];
for(int i=1;i<=M;i++) ANS=1ll*ANS*Y[i]_;
printf("%d",ANS);
return 0;
}

●BZOJ 4559 [JLoi2016]成绩比较的更多相关文章

  1. bzoj 4559 [JLoi2016]成绩比较 —— DP+拉格朗日插值

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4559 看了看拉格朗日插值:http://www.cnblogs.com/ECJTUACM-8 ...

  2. ●BZOJ 4559 [JLoi2016]成绩比较(容斥)

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4559 题解: 容斥,拉格朗日插值法. 结合网上的另一种方法,以及插值法,可以把本题做到 O( ...

  3. BZOJ.4559.[JLOI2016]成绩比较(DP/容斥 拉格朗日插值)

    BZOJ 洛谷 为什么已经9点了...我写了多久... 求方案数,考虑DP... \(f[i][j]\)表示到第\(i\)门课,还有\(j\)人会被碾压的方案数. 那么\[f[i][j]=\sum_{ ...

  4. bzoj 4559 [JLoi2016]成绩比较——拉格朗日插值

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4559 关于拉格朗日插值,可以看这些博客: https://www.cnblogs.com/E ...

  5. 【BZOJ】4559: [JLoi2016]成绩比较 计数DP+排列组合+拉格朗日插值

    [题意]n位同学(其中一位是B神),m门必修课,每门必修课的分数是[1,Ui].B神碾压了k位同学(所有课分数<=B神),且第x门课有rx-1位同学的分数高于B神,求满足条件的分数情况数.当有一 ...

  6. bzoj千题计划270:bzoj4559: [JLoi2016]成绩比较(拉格朗日插值)

    http://www.lydsy.com/JudgeOnline/problem.php?id=4559 f[i][j] 表示前i门课,有j个人没有被碾压的方案数 g[i] 表示第i门课,满足B神排名 ...

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

    4559: [JLoi2016]成绩比较 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 261  Solved: 165[Submit][Status ...

  8. bzoj 4557: [JLoi2016]侦察守卫 树归

    bzoj 4557: [JLoi2016]侦察守卫 设f[x][j]表示覆盖以x为根的子树的所有应该被覆盖的节点,并且以x为根的子树向下j层全部被覆盖的最小代价. 设g[x][j]表示与x距离大于j全 ...

  9. 【BZOJ4559】[JLoi2016]成绩比较 动态规划+容斥+组合数学

    [BZOJ4559][JLoi2016]成绩比较 Description G系共有n位同学,M门必修课.这N位同学的编号为0到N-1的整数,其中B神的编号为0号.这M门必修课编号为0到M-1的整数.一 ...

随机推荐

  1. 20145237 《Java程序设计》第八周学习总结

    20145237 <Java程序设计>第八周学习总结 教材学习内容总结 第十五章 通用API 15.1 日志 日志API简介 • java.util.logging包提供了日志功能相关类与 ...

  2. 深入分析Java Web中的编码问题

    编码问题一直困扰着我,每次遇到乱码或者编码问题,网上一查,问题解决了,但是实际的原理并没有搞懂,每次遇到,都是什么头疼. 决定彻彻底底的一次性解决编码问题. 1.为什么要编码 计算机的基本单元是字节, ...

  3. Linux安装mongodb总结

    由于自己的博客上线部署时需要用到mongodb来存储图片文件,所以先在本地电脑上安装了mongodb做测试,由于之前没接触过mongodb,所以安装过程中遇到了各种小问题,折腾了好久终于安装好并成功启 ...

  4. Java 后端微信小程序支付demo (网上说的坑里面基本上都有)

    Java 后端微信小程序支付 一.遇到的问题 1. 商户号该产品权限未开通,请前往商户平台>产品中心检查后重试 2.签名错误 3.已经调起微信统一下单接口,可以拿到预支付ID,但是前端支付的时候 ...

  5. EasyUI中, datagrid用loadData方法绑定数据。

    $("#dg").datagrid("loadData", { , " }, { "ck": "1", &qu ...

  6. 利用封装、继承对Java代码进行优化

    注:本文实例分别可以在oldcastle(未优化的代码)和newcastle(优化后的代码)中查看,网址见文末 城堡游戏: 城堡中有多个房间,用户通过输入north, south, east, wes ...

  7. python __str__ 和__repr__方法

    看下面的例子就明白了 class Test(object): def __init__(self, value='hello, world!'): self.data = value >> ...

  8. cookie中存中文

    cookie中存中文 1:想要在cookie中存中文:需要用到URLEncoder(在jdkAPI中有介绍) Cookie cookie = new Cookie("User",U ...

  9. 新概念英语(1-37)Making a bookcase

    What is Susan's favourite color ? A:You're working hard, Georage. What are you doing? B:I am making ...

  10. IDE-Ecplise-代码注释 模版 编码规范 配色

    说明: 代码注释主要用于方便代码后期维护,编码规范,增加代码阅读性和维护性.因网上看到的很多博客中片段局多,故整理后重写一篇,方便交流学习. 先看下加过注释模版后的效果. 如上图所示,创建类,方法和继 ...