@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. 详解WPF DockPanel的LastChildFill属性

    MSDN解释: 获取或设置一个值,该值指示 System.Windows.Controls.DockPanel 中的最后一个子元素是否拉伸以填充剩余的可用空间. 返回: 如果最后一个子元素拉伸以填充剩 ...

  2. flexget安装

    首先新建一个文件夹给flexget下载种子 Flexgetdownloads 注意:如果先安装flexget再创建文件夹则需要手动去分配权限 然后通过勾选我要试用beta版本,来获取flexget 找 ...

  3. org.hibernate.service.ServiceRegistryBuilder被弃用

    看视频教程是这样写的: //创建配置对象 Configuration config = new Configuration().configure(); //创建服务注册对象 ServiceRegis ...

  4. Django ORM中的查询,删除,更新操作

    ORM查询操作 修改views.py文件 from django.shortcuts import render, HttpResponse from app01 import models from ...

  5. hdu 1296 Polynomial Problem(多项式模拟)

    Problem Description We have learned how to obtain the value of a polynomial when we were a middle sc ...

  6. GYM100741 A Queries

    A. Queries time limit per test 0.25 s memory limit per test 64 MB input standard input output standa ...

  7. 洛谷P1244 [NOI2000] 青蛙过河 [2017年4月计划 动态规划07]

    P1244 青蛙过河 题目描述 有一条河,左边一个石墩(A区)上有编号为1,2,3,4,…,n的n只青蛙,河中有k个荷叶(C区),还有h个石墩(D区),右边有一个石墩(B区),如下图所示.n只青蛙要过 ...

  8. javaScript中的事件对象event是怎样

    事件对象event,每当一个事件被触发的时候,就会随之产恒一个事件对象event,该对象中主要包含了关于该事件的基本属性,事件类型type(click.dbclick等值).目标元素target(我的 ...

  9. 传统保险企业基于 Dubbo 的微服务实践

    本文整理自中国人寿保险(海外)股份有限公司深圳中心技术总监家黄晓彬在 Dubbo 社区开发者日深圳站的现场分享. 中国人寿保险(海外)股份有限公司负责香港.澳门.新加坡和印尼的业务开发,和国内业务不同 ...

  10. Ajax--同源策略,jsonp跨域传输原理(callback),

    什么是同源策略? 阮一峰的博客 同源策略 同源策略的解决方法: 跨域传输 img 标签的src是可以引入其他域名下的图片 script标签的src属性同理 ,也可以引入其他域名下的js文件,并执行 1 ...