如果这篇博客帮助到你,可以请我喝一杯咖啡~
CC BY-NC-SA 4.0 (除特别声明或转载文章外)
我们先把限制 $[l,r]$ 差分为 $[0,l)$ 和 $[0,r]$,这样问题转化为和不超过一个数 $u$ 的方案数。
根据插板法我们知道不考虑第二个限制的方案数为 $\sum\limits_{i=0}^u\binom{i-1+n}{n-1}=\binom{u+n}{n}$。
我们现在只需要求出不满足第二个限制的方案数 $S$ 减掉就可以了。设第 $m$ 位上的数为 $x$,枚举 $x$ 我们要求的就是有多少种序列使得 $m$ 个数 $\ge x$,$n-m$ 个数 $<x$,且至少有一个数 $=x$。至少有一个数 $=x$ 的限制是好处理的,只要求出 $m$ 个数 $\ge x+1$,$n-m$ 个数 $<x$ 的方案数减去即可。
问题转化为求解 $m$ 个数 $\ge a$,剩下的数 $< b$ 且和不超过 $u$ 的序列数,我们称其为 $f(a,b)$,由于插板法不易于处理 $<b$ 这样的限制,我们考虑钦定恰有 $i$ 个位置 $\ge b$,剩余的随意,这样的方案数就是 $\binom nm\binom{n-m}{i}\binom{u-ma-ib+n}{n}$,二项式反演可以得到,合法的方案数为 \(f(a,b)=\sum\limits_{i=0}^{n-m}(-1)^i\binom nm\binom{n-m}{i}\binom{u-ma-ib+n}{n}\) 于是有 \(S=\sum\limits_{x=1}^u(f(x,x)-f(x+1,x))\) 我们现在就获得了一个 $O(un)$ 的做法,但是我们会发现在 $f(a,b)$ 的式子中 $i$ 必须满足 $ib\le u-ma$,所以这里实际上是只有 $O(u\ln u)$ 种 $ib$ 有值的,于是我们就在 $O(u\ln u)$ 的复杂度内完成了 $S$ 的计算。
总复杂度 $O(R\log R+N)$。
#include<iostream>
using std::cin;
using std::cout;
int const mod=1e9+7;
int pow(int x,int y){
int res=1;
while(y){
if(y&1) res=1ll*res*x%mod;
x=1ll*x*x%mod,y>>=1;
}
return res;
}
int fac[600010],ifac[600010];
int C(int n,int m){
return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
void init(int n){
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=1ll*i*fac[i-1]%mod;
ifac[n]=pow(fac[n],mod-2);
for(int i=n;i;i--) ifac[i-1]=1ll*i*ifac[i]%mod;
}
int f(int a,int b,int n,int m,int u){
int ans=0;
for(int i=0;i<=n-m;i++){
long long p=u-1ll*m*a-1ll*i*b;
if(p<0) return ans;
ans=(ans+(i&1?mod-1ll:1ll)*C(n,m)%mod*C(n-m,i)%mod*C(p+n,n))%mod;
}
return ans;
}
int solve(int n,int m,int u){
int ans=C(u+n,n);
for(int i=1;i<=u;i++){
ans=(ans-f(i,i,n,m,u)+mod)%mod;
ans=(ans+f(i+1,i,n,m,u))%mod;
}
return ans;
}
int main(){
std::ios::sync_with_stdio(false),cin.tie(nullptr);
int n,m,l,r;
cin>>n>>m>>l>>r;
init(n+r);
cout<<(solve(n,m,r)-solve(n,m,l-1)+mod)%mod<<'\n';
return 0;
}