@description@

请找到满足以下条件的长度为 N 的非负整数序列 A1, A2, ..., AN 的数量。

(1)L≤A1+A2+...+AN≤R。

(2)将 N 个元素排成非增序列后,第 M 个元素要等于第 M + 1 个元素。

请将答案 mod 10^9 + 7。

Constraints

所有数都是整数。

1≤M<N≤3×10^5, 1≤L≤R≤3×10^5。

Input

输入形式如下:

N M L R

Output

输出序列数量 mod 10^9 + 7。

Sample Input 1

4 2 3 7

Sample Output 1

105

Sample Input 2

2 1 4 8

Sample Output 2

3

@solution@

即使放在最后一题,但其实这道题也是比较水的组合计数(所以为什么当时我做不起啊喂)。

令 f(N, M, S) 表示序列满足 0 <= A1 + A2 + ... + AN <= S 且满足题目所说的第二个条件时的答案,则最终答案 = f(N, M, R) - f(N, M, L - 1)。

先只考虑 A1 + A2 + ... + AN <= S 的条件,令 B = S - A1 - A2 - ... - AN,则 B >= 0 且 A1 + A2 + ... + AN + B = S。这样转化以后就可以把不等式化为等式,用隔板法可以快速统计出方案数(其实很像线性规划的标准型转松弛型)。

满足第二个条件,一种想法是用 \(C_N^M\) 算前 M 大的数所在的位置,将前 M 大与后 N-M 分开讨论。但是因为数可以相同,这样算出来会重复(即前 M 大可能跟后 N-M 有数字相同,会互相影响)。

但如果反过来,我们令排序后的 A'[M] ≠ A'[M+1]。因为排好序了,所以 A'[M] > A'[M+1] ,这样前 M 大与后 N-M 就分开来了。

通过计算强制不等于的方案数,用不带限制的方案数 - 强制不等于的方案数就可以得到答案。

枚举 A[M] = x,现在的限制转变为要求序列中有 M 个数 >= x,剩余 N - M 个数 < x,且至少要有一个数 = x。可以最后乘上系数 \(C_N^M\) 表示选择哪几个数 >= x。

注意到最后一个条件很碍眼,但是如果去掉可能会算重复。

我们这样来处理:用 “M 个数 >= x, N - M 个数 < x 的方案数” 减去 “M 个数 > x, N - M 个数 < x 的方案数”,即再次使用容斥。

现在问题转为:A1 + A2 + ... + AN + B = S;A[1], A[2], ... A[M] >= p;0 <= A[M+1], A[M+2], ... A[N] < q 的方案数。

限制 A[1], A[2], ... A[M] >= p 可以通过预先将 S 减去 M*p 来处理,这样就可以把 A[1...M] 的下界变成 0。

限制 A[M+1], A[M+2], ... A[N] < q 可以使用容斥,枚举强制选 k 个数 >= q,然后一样将 S 减去 k*q 将下界变成 0。乘上系数 \(C_{N-M}^{k}\) 表示选择哪 k 个数。

最后关于时间复杂度,因为 S - k*q >= 0 才有意义,所以最后的容斥是 O(S/q) 的复杂度,而其他地方的计算是常数级别。

而 q 是从 1...K 中枚举出来的数,所以时间复杂度为 O(S/1 + S/2 + ... + S/K)。因为 S 与 K 同阶,可以近似地看作 O(K log K)。

@accepted code@

#include<cstdio>
const int MAXN = 1000000;
const int MOD = int(1E9) + 7;
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;
}
int fct[MAXN + 5], ifct[MAXN + 5];
void init() {
fct[0] = 1;
for(int i=1;i<=MAXN;i++)
fct[i] = 1LL*fct[i-1]*i%MOD;
ifct[MAXN] = pow_mod(fct[MAXN], MOD-2);
for(int i=MAXN-1;i>=0;i--)
ifct[i] = 1LL*ifct[i+1]*(i+1)%MOD;
}
int comb(int n, int m) {return 1LL*fct[n]*ifct[m]%MOD*ifct[n-m]%MOD;}
int solve2(int N, int M, int S, int l, int r) {
int ret = 0;
if( S - 1LL*M*r < 0 ) return ret;
else S -= M*r;
for(int i=0,f=1;i<=N-M&&S>=0;i++,f=1LL*f*(MOD-1)%MOD,S-=l)
ret = (ret + 1LL*f*comb(S + N, N)%MOD*comb(N - M, i)%MOD)%MOD;
return 1LL*ret*comb(N, M)%MOD;
}
//A1 + A2 + ... + AN + B = S, A[1...M] >= r, 0 <= A[M+1...N] < l
//A1 + A2 + ... + AN + B = S - M*r, A[1...M] >= 0, 0 <= A[M+1...N] < l
int solve(int N, int M, int S) {
int ret = comb(S + N, N);
for(int i=S;i>=1;i--) {
int del = (solve2(N, M, S, i, i) + MOD - solve2(N, M, S, i, i + 1))%MOD;
ret = (ret + MOD - del)%MOD;
}
return ret;
}
//A1 + A2 + ... + AN + B = S
int main() {
init(); int N, M, L, R;
scanf("%d%d%d%d", &N, &M, &L, &R);
printf("%d\n", (solve(N, M, R) + MOD - solve(N, M, L - 1))%MOD);
}

@details@

比赛时看着 standings 感觉是一道不可做的题,然后考场上一直在想什么 fft 啊之类的。。。

赛后想了想,发现不对啊,这个不应该用组合数来做吗。

然后就想了一个解法,最后跟 editorial 对比发现基本一致。。。

这个题的每一步推导,都透露出满满的套路气息。。。

@atcoder - Japanese Student Championship 2019 Qualification - F@ Candy Retribution的更多相关文章

  1. @atcoder - Japanese Student Championship 2019 Qualification - E@ Card Collector

    目录 @description@ @solution@ @accepted code@ @details@ @description@ N 个卡片放在 H*W 的方格图上,第 i 张卡片的权值为 Ai ...

  2. [AtCoder] NIKKEI Programming Contest 2019 (暂缺F)

    [AtCoder] NIKKEI Programming Contest 2019   本来看见这一场的排名的画风比较正常就来补一下题,但是完全没有发现后两题的AC人数远少于我补的上一份AtCoder ...

  3. [AtCoder] Yahoo Programming Contest 2019

    [AtCoder] Yahoo Programming Contest 2019   很遗憾错过了一场 AtCoder .听说这场是涨分场呢,于是特意来补一下题. A - Anti-Adjacency ...

  4. Atcoder Tenka1 Programmer Contest 2019

    C 签到题,f[i][0/1]表示以i结尾最后一个为白/黑的最小值,转移显然. #include<bits/stdc++.h> using namespace std; ; ]; char ...

  5. atcoder NIKKEI Programming Contest 2019 E - Weights on Vertices and Edges

    题目链接:Weights on Vertices and Edges 题目大意:有一个\(n\)个点\(m\)条边的无向图,点有点权,边有边权,问至少删去多少条边使得对于剩下的每一条边,它所在的联通块 ...

  6. 【AtCoder】Tenka1 Programmer Contest(C - F)

    C - Align 考的时候,我大胆猜了结论,就是一小一大一小一大这么排 证明的话,由于我们总是要加上相邻的最大值而减去最小值,我们就让最大值都保持在前面 如果长度为奇数,要么就是大小大小大,要么是小 ...

  7. Atcoder Tenka1 Programmer Contest 2019 题解

    link 题面真简洁 qaq C Stones 最终一定是连续一段 . 加上连续一段 # .直接枚举断点记录前缀和统计即可. #include<bits/stdc++.h> #define ...

  8. Atcoder Tenka1 Programmer Contest 2019 E - Polynomial Divisors

    题意: 给出一个多项式,问有多少个质数\(p\)使得\(p\;|\;f(x)\),不管\(x\)取何值 思路: 首先所有系数的\(gcd\)的质因子都是可以的. 再考虑一个结论,如果在\(\bmod ...

  9. Atcoder Tenka1 Programmer Contest 2019 D Three Colors

    题意: 有\(n\)个石头,每个石头有权值,可以给它们染'R', 'G', 'B'三种颜色,如下定义一种染色方案为合法方案: 所有石头都染上了一种颜色 令\(R, G, B\)为染了'R', 染了'G ...

随机推荐

  1. 使用 git 及 github

    1.github 开户.创建项目 2.以下是本地操作: #初始本地库 git init#设置git的全局邮箱和用户名git config --global user.email "<e ...

  2. 序列化form表单

    // 序列化JSON字符串 $.fn.serializeObject = function () { let o = {}; let a = this.serializeArray(); $.each ...

  3. 解决VC编译的DLL要安装对应运行环境否则无法使用的问题

    编译时候改成静态链接,再通过使用Depends软件查看依赖即可.具体操作步骤:项目->属性->配置属性->C/C++-代码生成->运行库->多线程(/MT)说明:这种方法 ...

  4. 【模板】 递归线段树 [2017年五月计划 清北学堂51精英班Day4]

    P3372 [模板]线段树 1 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别 ...

  5. 【weex】h5weex-example

    这个就是一个练手的基础性的demo,不过也是有很多值得学习的东西的 效果如下 项目地址为:https://github.com/h5weex/h5weex-example 可能是我找到的项目比较少,很 ...

  6. python枚举详解

    1. 枚举的定义 首先,定义枚举要导入enum模块. 枚举定义用class关键字,继承Enum类. 用于定义枚举的class和定义类的class是有区别[下一篇博文继续分享]. 示例代码: from ...

  7. 一款你不容错过的Laravel后台管理扩展包 —— Voyager – Laravel学院

    1.简介 Voyager是一个你不容错过的Laravel后台管理扩展包,提供了CRUD操作.媒体管理.菜单构建.数据管理等操作. 官网:https://the-control-group.github ...

  8. WPF:数据绑定总结(1) https://segmentfault.com/a/1190000012981745

    WPF:数据绑定总结(1) visual-studio c# 1.3k 次阅读  ·  读完需要 16 分钟 0 一.概念:什么是数据绑定? WPF中的数据绑定:是在应用程序 UI 与业务逻辑之间建立 ...

  9. babel 7.x 结合 webpack 4.x 配置

    今天在学习webpack的使用的时候,由于学习的教程是2018年初的,使用的是 webpack 3.x 和 babel 6.x ,然后学习的过程中出现的了很多问题. 解决问题之后,总结一下新的 bab ...

  10. python 布尔值索引