Portal -->bzoj4559

补档计划

​   借这题补个档——拉格朗日插值

​​   插值的话大概就是有一个\(n-1\)次多项式\(A(x)\),你只知道它在\(n\)处的点值,分别是\((x_1,y_1),(x_2,y_2),...,(x_{n},y_{n})\),让你还原这个多项式

​   关于插值我们有很多的方法,有十分粗暴的高消\(O(n^3)\),也有十分优秀的快速差值(不会qwq),还有拉格朗日插值法\(O(n^2)\)(emmm如果说取值位置连续的话。。可以做到\(O(n)\))

​​   其实拉格朗日差值的核心就是这两条式子:

\[\begin{aligned}
l_j(x)&=\sum\limits_{i=0,i\neq j}^n\frac{x-x_i}{x_j-x_i}\\
A(x)&=\sum\limits_{i=1}^{n}y_i\cdot l_i(x)
\end{aligned}
\]

​​   具体为什么的话就是,首先有\(n\)个处点值是可以确定一个\(n-1\)次多项式,并且这个多项式是惟一的,那么接下来就是看为什么这样构造是满足条件的:我们观察\(l_j(x)\)这个多项式的值,当代进去的值为\(x_i\)中的其中一个时,这个多项式才会为\(1\),否则为\(0\),所以乘上一个\(y_i\)然后求一下和就可以满足这\(n\)处的取值了

​​   然后就是这个东西怎么比较。。快一点的求

​   大概是。。我们可以先求出\(C(x)=\sum\limits_{i=1}^n(x-x_i)\)这个多项式各项系数(可以在\(O(n)\)的时间内得到,具体的话就是考虑每次暴力拆括号然后未知数的幂指数\(+1\),然后系数要乘上\(-x_i\)),求出这个东西之后,我们就可以在\(O(n)\)的时间内求出\(B_j(x)=\sum\limits_{i=1,i\neq j}^n(x-x_i)\)(具体就是直接考虑将\((x-x_j)\)这项的贡献去掉就好了),然后我们又可以用\(B_j(x)\)求出分母然后其他的随便乱搞都可以了

​   那为什么说取值位置连续的话可以\(O(n)\)呢?其实。。因为是连续的所以我们的分子可以用阶乘什么的乱搞,然后分母维护一个前缀乘积,一个后缀乘积就好了(实际上好像前缀成绩后缀乘积这个东西完全可以在非连续情况下使用但是反正分母那里要\(O(n)\)又何必搞那么麻烦呢嘿嘿。。。)

​​   这里贴一下非连续取值的时候的代码(虽然说这题根本不用不过反正都是补档嘛。。一起写好了)

void solve(int n){
memset(c,0,sizeof(c));
c[0]=1;
for (int i=1;i<=n;++i){
for (int j=n;j>=0;--j){
c[j+1]=(c[j+1]+c[j])%MOD;
c[j]=(-c[j]*X[i])%MOD;
}
}//get 系数 of \prod_{i=0}^k (x-x_i)
ll s=0,tmp=1;
for (int i=1;i<=n;++i){
memcpy(d,c,sizeof(d));
memset(b,0,sizeof(b));
for (int j=n;j>=0;--j){
b[j]=d[j+1];
d[j]=(d[j]+d[j+1]*X[i])%MOD;
d[j+1]=0;
}//take (x-x_i) out of \prod_{j=0}^k (x-x_j)
s=0,tmp=1;
for (int j=0;j<=n;++j){
s=(s+tmp*b[j])%MOD;
tmp=tmp*X[i]%MOD;
}//get \prod_{j=0,j!=i}^k (x_i-x_j) s=ksm(s,MOD-2)*Y[i]%MOD;
for (int j=0;j<=n;++j) b[j]=b[j]*s%MOD;//get the final 系数
for (int j=0;j<=n;++j)
ans[j]=(ans[j]+b[j])%MOD;
}
}

​  

Solution

​​   然后我们来看这题

​​   这题的做法很多。。网上有十分优秀的根本不需要拉格朗日插值的做法。。但是我比较菜所以。。

​​   考虑一下这个答案要怎么算,因为题目的限制条件有点多所以我们考虑将它拆成两个部分来想:

1.先不管具体分数是多少,只考虑相对大小,求出满足有恰好\(K\)个人各门分数都小于等于\(B\)神的方案数,记为\(ans1\)

2.算具体分数是多少

​​   那么最后的答案就应该是\(ans1\)再乘上对应的第二部分中具体分数不同的贡献

​​   我们先来考虑第一部分,这里。。我们考虑用dp+容斥来求解(注意,接下来的dp中所有的转移都不考虑每个人的具体得分,我们在第一部分的求解中只考虑相对大小)

​​   记\(f[i]\)表示至少\(i\)个人满足条件的方案数,那么我们可以得到:

\[f[i]=\binom{n-1}i \prod\limits_{j=1}^m \binom{n-1-i}{R_j-1}
\]

​​   其中\(R_j\)表示的是\(B\)神在第\(j\)门科目中的排名,这个式子具体一点的含义的话就是:首先我们需要钦定\(i\)个人是满足条件的,所以总共是\(\binom {n-1}i\)中选法,然后在钦定了\(i\)个人一定排在\(B\)神之后的前提下,我们对于每一门科目又要满足有\(R_j-1\)个人分数大于\(B\)神,那只能从\(n-1-i\)个人中选,所以就是后面的按个组合数了

​   有了这个东西之后我们构造一下容斥系数\(a(i)\):

\[ans1=\sum\limits_{i=K}^na(i)f[i]
\]

​​   其实仔细想一下就可以得出:

\[ans1=\sum\limits_{i=K}^n(-1)^{K-i}\binom i Kf[i]
\]

​​   具体一点就是你考虑\(f[i]\)的含义就是至少\(i\)个人满足条件,那么这个\(K\)个人中一旦有\(i\)个人是被钦定的,\(f[i]\)就会被算一次,所以乘个系数就好了

​  

​​   然后第一部分我们就算完了

​​   接着是第二部分(这部分才到插值==)

​​   第二部分的话,我们要考虑每个人具体是什么分数了,我们要求的东西其实就是要求有\(x\)个人比\(B\)神低,\(y\)个人比\(B\)神高的分数有多少种(与第一部分不同的是,这里不需要考虑具体是哪一个人,而是考虑分数)

​​   那么对于一门科目\(i\),我们有\(y=R_i-1,x=(n-1)-y\), 所以我们只要枚举一下\(B\)神的分数就好了:

\[\sum\limits_{j=1}^{U_i}(U_i-j)^{R_i-1}\cdot j^{(n-1)-(R_i-1)}
\]

​​   其中\(U_i\)表示这门科目分数上限

​​   那么所有的科目的总贡献就是:

\[ans2=\prod\limits_{i=1}^m\sum\limits_{j=1}^{U_i}(U_i-j)^{R_i-1}\cdot j^{(n-1)-(R_i-1)}
\]

​​   最终答案就是\(ans1*ans2\)啦

​​   但是我们看\(U_i\)的范围是\(10^9\),所以。。不能直接算,又发现如果说我们将\(U_i\)看成一个变量的话,每门科目的贡献可以看成一个\(n\)次多项式\(P(x)\)(次数最高只能到\(n\)),那所以我们可以先暴力带入\(x=0...n\),然后把这\(n+1\)个点值算出来,然后每次\(O(n^2)\)求值就好了(具体的话就是直接代进拉格朗日插值的那条式子)

​​   总的时间复杂度\(O(n^3)\)(其实。。求值好像可以做到每次\(O(n)\)的。。不过\(n=100\)我就写了最暴力的这种了qwq)

​ ​  

​   代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=110,MOD=1e9+7;
int u[N],r[N],f[N],c[N][N],g[N];
int n,m,K,ans1,ans2;
int add(int x,int y){return (1LL*x+y)%MOD;}
int mul(int x,int y){return 1LL*x*y%MOD;}
void predp(){
c[0][0]=1;
for (int i=1;i<=n;++i){
c[i][0]=1; c[i][i]=1;
for (int j=1;j<i;++j)
c[i][j]=add(c[i-1][j],c[i-1][j-1]);
}
for (int i=1;i<=n;++i){
f[i]=c[n-1][i];
for (int j=1;j<=m;++j)
f[i]=mul(f[i],c[n-1-i][r[j]-1]);
}
}
int ksm(int x,int y){
int ret=1,base=x;
for (;y;y>>=1,base=mul(base,base))
if (y&1) ret=mul(ret,base);
return ret;
}
void precalc(int U,int R,int &val){
int tmp;
val=0;
for (int i=1;i<=U;++i){
tmp=mul(ksm(U-i,R-1),ksm(i,(n-1)-(R-1)));
val=add(val,tmp);
}
}
int calc(int x){
int fz,fm,ret=0;
for (int i=0;i<=n;++i){
fz=1; fm=1;
for (int j=0;j<=n;++j){
if (j==i) continue;
fz=mul(fz,(x-j+MOD)%MOD);
fm=mul(fm,(i-j+MOD)%MOD);
}
ret=add(ret,1LL*fz*ksm(fm,MOD-2)%MOD*g[i]%MOD);
}
return ret;
} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d%d%d",&n,&m,&K);
for (int i=1;i<=m;++i) scanf("%d",u+i);
for (int i=1;i<=m;++i) scanf("%d",r+i);
predp();
ans1=0;
for (int i=K;i<=n;++i){
if ((K-i)%2==0)
ans1=add(ans1,mul(c[i][K],f[i]));
else
ans1=add(ans1,(1LL*MOD-mul(c[i][K],f[i]))%MOD);
}
ans2=1;
for (int i=1;i<=m;++i){
if (i==6)
int debug=1;
for (int j=0;j<=n;++j)
precalc(j,r[i],g[j]);
ans2=mul(ans2,calc(u[i]));
}
printf("%d\n",mul(ans1,ans2));
}

【bzoj4559】成绩比较的更多相关文章

  1. BZOJ4559 成绩比较

    题目传送门 分析: 我们可以先试着求一下,对于单个学科,有多少种分配方案可以使B神排名为R 对于第i个学科 \(~~~~g(i)=\sum_{j=1}^{H_i}j^{n-R_i}(H_i-j)^{R ...

  2. 【BZOJ4559】成绩比较(动态规划,拉格朗日插值)

    [BZOJ4559]成绩比较(动态规划,拉格朗日插值) 题面 BZOJ 洛谷 题解 显然可以每门课顺次考虑, 设\(f[i][j]\)表示前\(i\)门课程\(zsy\)恰好碾压了\(j\)个\(yy ...

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

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

  4. 【BZOJ4559】成绩比较(组合计数,容斥原理)

    题意: G系共有n位同学,M门必修课.这N位同学的编号为0到N-1的整数,其中B神的编号为0号.这M门必修课编号为0到M- 1的整数.一位同学在必修课上可以获得的分数是1到Ui中的一个整数.如果在每门 ...

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

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

  6. BZOJ4559: [JLoi2016]成绩比较(dp 拉格朗日插值)

    题意 题目链接 Sol 想不到想不到.. 首先在不考虑每个人的真是成绩的情况下,设\(f[i][j]\)表示考虑了前\(i\)个人,有\(j\)个人被碾压的方案数 转移方程:\[f[i][j] = \ ...

  7. BZOJ4559 JLOI2016成绩比较(容斥原理+组合数学+斯特林数)

    容斥一发改为计算至少碾压k人的情况数量,这样对于每门课就可以分开考虑再相乘了.剩下的问题是给出某人的排名和分数的值域,求方案数.枚举出现了几种不同的分数,再枚举被给出的人的分数排第几,算一个类似斯特林 ...

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

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

  9. 【bzoj4559】[JLoi2016]成绩比较(dp+拉格朗日插值)

    bzoj 题意: 有\(n\)位同学,\(m\)门课. 一位同学在第\(i\)门课上面获得的分数上限为\(u_i\). 定义同学\(A\)碾压同学\(B\)为每一课\(A\)同学的成绩都不低于\(B\ ...

随机推荐

  1. json简单操作

    通过内置的json模块对json数据进行编码 1.对数据进行编码(dumps) import json #使用dumps将python数据结构转换为json data = { , "name ...

  2. datax 执行流程分析

    https://www.jianshu.com/nb/29319571 https://www.jianshu.com/p/b10fbdee7e56

  3. Linux内核学习笔记(1)-- 进程管理概述

    一.进程与线程 进程是处于执行期的程序,但是并不仅仅局限于一段可执行程序代码.通常,进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个 ...

  4. 华为笔试——C++转换字符串问题

    题目:转换字符串 题目介绍: 将输入字符串中下标为偶数的字符连成一个新的字符串输出,需要注意两点: 1. 如果输入字符串的长度超过20,则转换失败,返回“ERROR!”字符串: 2. 输入字符串只能由 ...

  5. loadrunner--基础2

    LR11-03 一.并发测试(n VU) 1.并发测试两个条件 1)脚本中要有 集合点(并发点) 2)控制台中要设置并发策略(选择第一项,所有虚拟用户到达集合点后释放) 集合点: 5个线程,代表5个V ...

  6. 《Linux内核与分析》第六周

    20135130王川东 1.操作系统的三大管理功能包括:进程管理,内存管理,文件系统. 2. Linux内核通过唯一的进程标识PID来区别每个进程.为了管理进程,内核必须对每个进程进行清晰的描述,进程 ...

  7. Hadoop环境搭建01

    根据马士兵老师的Hadoop进行的配置 1.首先列下来需要用到的软件 VirtulBox虚拟机.Centos7系统镜像.xshell.xftp.jdk安装包.hadoop-2.7.0安装包 2.在Vi ...

  8. Android开发随笔2

    昨天:对anroid的系统架构了解比如:基于linux内核,整合库函数和java编译器并且为上层提供封装好的api和一些基本系统级应用 创建一个安卓的模拟器 了解了ddms的作用和内容 利用已有的工具 ...

  9. 《我是一只IT小小鸟》心得

    虽然读这本书是老师布置的作业,但是读了几页后就被书中的内容所吸引住了.或许是因为我也是学这个专业的,所以书中的一些内容让我觉得非常的有兴趣.作为一个学习软件工程的大一学生还没真正的认识到这个专业的深奥 ...

  10. 模拟jq的设置样式

    //需求,创建一个div,添加到页面上,给div添加属性 //面向对象开发,构造函数创建类 function divTag(){ this.div1=document.createElement('d ...