@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. stackless 安装

    1.下载源码 https://bitbucket.org/stackless-dev/stackless/wiki/Download 2.编译.安装.路径生效 apt-get install libr ...

  2. 【arc077f】AtCoder Regular Contest 074 F - Lotus Leaves

    题意 给定一个n*m的池塘,每个格子上可能有叶子. 从一个叶子出发,可以跳到相同行或相同列的叶子. 问至少去掉多少叶子,使得起点不能到达终点. \(n,m<=100\) 解法 很显然的最小割模型 ...

  3. nginx 封ip

    封ip段 例如ip为:xx.xx.xx.xx 全封:0.0.0.0/0 封后三段:xx.0.0.0/8 封后两段:xx.xx.0.0/16 封最后一段:xx.xx.xx.0/24

  4. jnhs中国省市县区mysql数据表不带gps坐标

    1.查省 SELECT * FROM china WHERE china.Pid=0 2.查市 SELECT * FROM chinaWHERE china.Pid=330000 3.查区 SELEC ...

  5. Zookeeper安装过程

    zookeeper的安装我反反复复安装了三次,前两次在root用户下安装都失败了,都启动不起来,第三次我改成普通用户安装,没想到成功了,很不可思议,步骤完全一样,接下来介绍一下具体的安装步骤: 1. ...

  6. 洛谷P2381 圆圆舞蹈

    P2381 圆圆舞蹈 题目描述 熊大妈的乃修在时针的带领下,围成了一个圆圈舞蹈,由于没有严格的教育,奶牛们之间的间隔不一致. 奶牛想知道两只最远的奶牛到底隔了多远.奶牛A到B的距离为A顺时针走和逆时针 ...

  7. 【洛谷P1204】【USACO1.2】挤牛奶Milking Cows

    P1204 [USACO1.2]挤牛奶Milking Cows 题目描述 三个农民每天清晨5点起床,然后去牛棚给3头牛挤奶.第一个农民在300秒(从5点开始计时)给他的牛挤奶,一直到1000秒.第二个 ...

  8. Leetcode665.Non-decreasing Array非递减数组

    给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列. 我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i < n ...

  9. tp5.1 本地正常, 线上route.php不起作用的问题

    由于本项目 的.htaccess文件是放在根目录的, 上传没有覆盖,重新编辑 <IfModule mod_rewrite.c> Options +FollowSymlinks -Multi ...

  10. 100个常用的原生JavaScript函数

    1.原生JavaScript实现字符串长度截取 复制代码代码如下: function cutstr(str, len) {    var temp;    var icount = 0;    var ...