@description@

请你找到 k 个不同的组合数,使得对于其中任何一个组合数 \(C_a^b\) 有 \(0\leq b\leq a\leq n\)。所谓不同的组合数,即对于组合数 \(C_{a_1}^{b_1}\) 和 \(C_{a_2}^{b_2}\) ,若 \(a_1\neq a_2\) 或者 \(b_1\neq b_2\) ,则我们认为这两个组合数是不同的。问这 \(k\) 个组合数的和最大是多少?

input

第一行两个整数 n, k。

output

一行一个整数,代表 k 个组合数的和对 10^9+7 取模之后的结果;数据保证一定有至少 k 个数可以选。

sample input

2 3

sample output

4

对于 \(20\%\) 的数据,\(n\leq 10\)。

对于 \(40\%\) 的数据,\(n\leq 500\)。

对于另外 \(20\%\) 的数据,\(k=1\)。

对于 \(100\%\) 的数据, \(1\leq n\leq 10^6,1\leq k\leq 10^5\) 。

@solution@

问题相当于求前 k 大的组合数。

当 \(a < b < m/2\) 或 \(a > b > m/2\) 时,有 \(C_{m}^a < C_m^{b}\);

当 \(a < b\) 时,有 \(C_{a}^p < C_{b}^p\)。

这是可以从杨辉三角中看出来的。

我们可以把最大的那个组合数 \(C_n^{n/2}\) 加入优先队列 ,然后向四周扩展。每一次从优先队列中取出最大值 \(C_{a}^{b}\),扩展出 \(C_{a-1}^{b}\), \(C_{a}^{b-1}\),\(C_{a}^{b+1}\),然后将它们加入优先队列。扩展 k 次即可。

同时要注意不要重复经过某一个点。开一个 set 判一下重。

那么问题来了:我们的组合数是取了模的,塞在优先队列里面怎么比较大小呢?逼我写高精度?

这个时候,一个闻所未闻的操作就来了:两边同时取对数

我们组合数公式长这样:

\[C_n^m=\dfrac{n!}{m!*(n-m)!}
\]

取完对数长这样:

\[\log_2 C_n^m=\sum_{i=1}^n\log_2 i-\sum_{i=1}^m\log_2 i-\sum_{i=1}^{n-m}\log_2 i
\]

(当然底数不一定为 2)

可以发现这个式子是绝对不会溢出的,而且还可以前缀和预处理。

又因为底数大于 1,所以对数的大小关系等同于原数的大小关系。

所以我们就可以比较组合数之间的大小了。

那么问题来了:double 的精度真的不会翻车吗_(:з」∠)_?

@accepted code@

#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<iostream>
using namespace std;
const int MOD = int(1E9) + 7;
const int MAXN = 1000000;
int fct[MAXN + 5], inv[MAXN + 5];
double lgsum[MAXN + 5];
int pow_mod(int b, int p) {
int ret = 1;
while( p ) {
if( p & 1 ) ret = 1LL*ret*b%MOD;
b = 1LL*b*b%MOD;
p >>= 1;
}
return ret;
}
void init() {
fct[0] = 1;
for(int i=1;i<=MAXN;i++)
fct[i] = 1LL*fct[i-1]*i%MOD;
inv[MAXN] = pow_mod(fct[MAXN], MOD-2);
for(int i=MAXN-1;i>=0;i--)
inv[i] = 1LL*inv[i+1]*(i+1)%MOD;
lgsum[1] = log2(1);
for(int i=2;i<=MAXN;i++)
lgsum[i] = lgsum[i-1] + log2(i);
}
int C(int n, int m) {
return 1LL*fct[n]*inv[m]%MOD*inv[n-m]%MOD;
}
struct node{
int n, m;
node(int _n=0, int _m=0):n(_n), m(_m){}
};
bool operator < (node a, node b) {
if( lgsum[a.n] - lgsum[a.m] - lgsum[a.n - a.m] != lgsum[b.n] - lgsum[b.m] - lgsum[b.n - b.m] )
return lgsum[a.n] - lgsum[a.m] - lgsum[a.n - a.m] < lgsum[b.n] - lgsum[b.m] - lgsum[b.n - b.m];
else return (a.n == b.n) ? a.m < b.m : a.n < b.n;
}
set<node>Set;
priority_queue<node>que;
int main() {
int n, k, ans = 0; init();
scanf("%d%d", &n, &k);
node s = node(n, n/2);
Set.insert(s), que.push(s);
for(int i=1;i<=k;i++) {
s = que.top(); que.pop();
ans = (ans + C(s.n, s.m))%MOD;
if( s.m != 0 ) {
if( !Set.count(node(s.n, s.m - 1)) )
Set.insert(node(s.n, s.m - 1)), que.push(node(s.n, s.m - 1));
}
if( s.m != s.n ) {
if( !Set.count(node(s.n, s.m + 1)) )
Set.insert(node(s.n, s.m + 1)), que.push(node(s.n, s.m + 1));
if( !Set.count(node(s.n - 1, s.m)) )
Set.insert(node(s.n - 1, s.m)), que.push(node(s.n - 1, s.m));
}
}
printf("%d", ans);
}

@details@

重新刷新了 double 的精度问题。

MD 每次我写二分你都卡我精度你这次偏偏不会卡这玩意儿的精度?

取对数这种操作也不是没有见过,《麦森数》那道题就有用到。

看来印象不深刻 QAQ……我果然还是太弱了 QAQ。

@loj - 6353@「CodePlus 2018 4 月赛」组合数问题 2的更多相关文章

  1. LOJ#6354. 「CodePlus 2018 4 月赛」最短路[最短路优化建图]

    题意 一个 \(n\) 个点的完全图,两点之间的边权为 \((i\ xor\ j)*C\) ,同时有 \(m\) 条额外单向路径,问从 \(S\) 到 \(T\) 的最短路. \(n\leq 10^5 ...

  2. loj #6302. 「CodePlus 2018 3 月赛」寻找车位【线段树+单调队列】

    考虑静态怎么做:枚举右边界,然后枚举上边界,对应的下边界一定单调不降,单调栈维护每一列从当前枚举的右边界向左最长空位的长度,这样是O(nm)的 注意到n>=m,所以m<=2000,可以枚举 ...

  3. @loj - 6354@「CodePlus 2018 4 月赛」最短路

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 企鹅国中有 N 座城市,编号从 1 到 N . 对于任意的两座城 ...

  4. 【LibreOJ】#6354. 「CodePlus 2018 4 月赛」最短路 异或优化建图+Dijkstra

    [题目]#6354. 「CodePlus 2018 4 月赛」最短路 [题意]给定n个点,m条带权有向边,任意两个点i和j还可以花费(i xor j)*C到达(C是给定的常数),求A到B的最短距离.\ ...

  5. [LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞

    [LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞 试题描述 到河北省 见斯大林 / 在月光下 你的背影 / 让我们一起跳舞吧 うそだよ~ 河北省怎么可能有 Stalin. ...

  6. loj #6250. 「CodePlus 2017 11 月赛」找爸爸

    #6250. 「CodePlus 2017 11 月赛」找爸爸 题目描述 小 A 最近一直在找自己的爸爸,用什么办法呢,就是 DNA 比对. 小 A 有一套自己的 DNA 序列比较方法,其最终目标是最 ...

  7. [LOJ 6249]「CodePlus 2017 11 月赛」汀博尔

    Description 有 n 棵树,初始时每棵树的高度为 H_i,第 i 棵树每月都会长高 A_i.现在有个木料长度总量为 S 的订单,客户要求每块木料的长度不能小于 L,而且木料必须是整棵树(即不 ...

  8. [LOJ 6248]「CodePlus 2017 11 月赛」晨跑

    Description “无体育,不清华”.“每天锻炼一小时,健康工作五十年,幸福生活一辈子” 在清华,体育运动绝对是同学们生活中不可或缺的一部分.为了响应学校的号召,模范好学生王队长决定坚持晨跑.不 ...

  9. loj6300 「CodePlus 2018 3 月赛」博弈论与概率统计

    link 题意: A和B玩游戏,每轮A赢的概率为p.现在有T组询问,已知A赢了n轮输了m轮,没有平局,赢一局A得分+1,输一局得分-1,问A得分期望值? $n+m,T\leq 2.5\times 10 ...

随机推荐

  1. jQ-点击查看更多

    <style type="text/css"> .hi { width: 200px; height: 18vw; background-color: pink; fo ...

  2. nginx在win系统下的安装配置与tomcat集成springmvc框架

    先来一个常用命令 验证配置是否正确: nginx -t 查看Nginx的版本号:nginx -V 启动Nginx:start nginx 快速停止或关闭Nginx:nginx -s stop 正常停止 ...

  3. Struts_客户列表练习

    1.导包 2.配置文件struts.xml 3.创建CustomerAction 4.修改menu.jsp和List.jsp 5.配置web.xml

  4. C++ 之手写memcpy

    #include<iostream>#include<cstdio>using namespace std; void* mymemcpy(void* dst, const v ...

  5. String int 变量互相转化

    int -> String int i=12345;String s="";第一种方法:s=i+"";第二种方法:s=String.valueOf(i); ...

  6. SDUT-3399_数据结构实验之排序二:交换排序

    数据结构实验之排序二:交换排序 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 冒泡排序和快速排序都是基于"交 ...

  7. 10分钟学会Python

    #1. 语法 Python中没有强制的语句终止字符,代码块是通过缩进来指示的.缩进表示一个代码块的开始,逆缩进则表示一个代码块的结束.一般用4个空格来表示缩进. 声明以冒号(:)字符结束,并且开启一个 ...

  8. kubernetes1.4新特性:支持两种新的卷插件

    背景介绍 在Kubernetes中卷的作用在于提供给POD持久化存储,这些持久化存储可以挂载到POD中的容器上,进而给容器提供持久化存储. 从图中可以看到结构体PodSpec有个属性是Volumes, ...

  9. 【JZOJ4790】【NOIP2016提高A组模拟9.21】选数问题

    题目描述 在麦克雷的面前有N个数,以及一个R*C的矩阵.现在他的任务是从N个数中取出R*C个,并填入这个矩阵中.矩阵每一行的法值为本行最大值与最小值的差,而整个矩阵的法值为每一行的法值的最大值.现在, ...

  10. Nginx教程(二) Nginx虚拟主机配置 (转)

    Nginx教程(二) Nginx虚拟主机配置 1 虚拟主机管理 1.1 Nginx管理虚拟主机 虚拟主机使用的是特殊的软硬件技术,它把一台运行在因特网上的服务器主机分成一台台“虚拟”的主机,每台虚拟主 ...