题链:

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. 冲刺NO.1

    Alpha冲刺第一天 站立式会议 项目进展 项目的第一天,主要工作是对项目的开发进行规划,以及将规划的成果转化为燃尽图与博客文章.依据项目需求分析报告与开题报告中已经完成的设计任务和项目规划,我们将系 ...

  2. Git学习使用

    1.注册码云并建立远程仓库 2.安装git 3.使用eclipse egit 推送以及克隆 建立本地仓库,成功后如图 推送项目至本地仓库与远程仓库 使用右键菜单team-share 选项,与仓库关联后 ...

  3. window.showModalDialog

    //新版本谷歌没有window.showModalDialog,创建一个window.openif(window.showModalDialog == undefined){  window.show ...

  4. OpenShift实战(一):OpenShift高级安装

    1.1 服务器基本信息 本次安装采用一个master.5个node.3个etcd,node节点两块硬盘,60G磁盘用于docker storage,xxx改为自己的域名或主机名. 节点 功能 IP 内 ...

  5. 新手入门 git

    Git是目前世界上最先进的分布式版本控制系统 特点:高端大气上档次 什么是版本控制系统 系统自动记录文件改动 方便同事协作管理 不用自己管理一堆类似的文件了,也不需要把文件传来传去.如果想查看某次改动 ...

  6. JAVA_SE基础——46.引用数据类型变量.值交换[独家深入解析]

    需求:定义一个函数交换数组中两个元素的位置. code 1: import java.util.*; class Demo3 { public static void main(String[] ar ...

  7. oracle导入命令,记录一下

    工作中用到了,这个命令,记录一下,前提要安装imp.exe imp PECARD_HN/PECARD_HN@127.0.0.1:1521/orcl file=E:\work\dmp\PECARD_HN ...

  8. ELK学习总结(2-5)elk的版本控制

    ----------------------------------------------------------------- 1.悲观锁和乐观锁 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据 ...

  9. Spring Security入门(2-3)Spring Security 的运行原理 3

    关键组件关系 FilterSecurityInterceptor--- authenticationManager --- UserDetailService--- accessDecisionMan ...

  10. 基于UDP协议的控制台聊天程序(c++版)

    本博客由Rcchio原创,转载请告知作者 ------------------------------------------------------------------------------- ...