在洛谷没找到对应的题,不放链接了
然后我bzoj的号弄丢了,所以原题链接也不放了,要的话自己去找吧
题目大意
给定三个正整数 n , l , r n,l,r n,l,r ,统计长度 l e n ∈ [ 1 , n ] ∩ Z len\in [1,n]\cap\Z len∈[1,n]∩Z 且元素大小 x x x 均满足 l ≤ x ≤ r l\le x\le r l≤x≤r 的单调不降序列的数量。答案对 1 0 6 + 3 10^6+3 106+3 取模。
(你需要处理 T T T 个询问)
思路
对于单调不降序列我们是没有比较好的方法处理的,所以将它的第
i
i
i 个元素加上
i
i
i ,这样问题就变成了求单调上升序列的数量。
此时元素值域为
l
+
1
≤
x
≤
r
+
l
e
n
.
l+1 \le x \le r+len.
l+1≤x≤r+len.
显然
[
l
+
1
,
r
+
l
e
n
]
[l+1,r+len]
[l+1,r+len] 区间内的整数有
r
−
l
+
l
e
n
r-l+len
r−l+len 个。方便起见我们设
t
=
r
−
l
.
t=r-l.
t=r−l.
从中选出
l
e
n
len
len 个数构成一个序列的方案数是
C
t
+
l
e
n
l
e
n
=
C
t
+
l
e
n
t
C^{len}_{t+len}=C^t_{t+len}
Ct+lenlen=Ct+lent 。每种方案都恰好对应一种单调上升序列,所以这也是答案。
最后的答案就是
∑
l
e
n
=
1
n
C
t
+
l
e
n
t
.
\sum\limits_{len=1}^nC^t_{t+len}.
len=1∑nCt+lent.
当然你拿着这个式子直接爆算是绝对不行的。我们需要继续化简。
在它的前面添上一项
C
t
+
1
t
+
1
=
1
C^{t+1}_{t+1}=1
Ct+1t+1=1 ,发现
C
t
+
1
t
+
1
+
C
t
+
1
t
=
C
t
+
2
t
+
1
C^{t+1}_{t+1}+C^t_{t+1}=C^{t+1}_{t+2}
Ct+1t+1+Ct+1t=Ct+2t+1 ,再利用滚雪球的计算方式(俗名应该叫这个)就可以得到
原
式
+
C
t
+
1
t
+
1
=
C
t
+
n
+
1
t
+
1
.
原式+C^{t+1}_{t+1}=C^{t+1}_{t+n+1}.
原式+Ct+1t+1=Ct+n+1t+1.
最后整理一下再把 t = r − l t=r-l t=r−l 代回去就有 a n s = C n + r − l + 1 r − l + 1 − 1. ans=C^{r-l+1}_{n+r-l+1}-1. ans=Cn+r−l+1r−l+1−1. 这个好办, 1 0 6 + 3 10^6+3 106+3 是个质数, L u c a s \rm Lucas Lucas 定理上一波就好了。
#include<cstdio>
const int p=1e6+3; //模数
int _power(int x,int y) { //快速幂
int ans=1;
while (y) {
if (y&1) ans=1ll*ans*x%p;
x=1ll*x*x%p; y>>=1;
}
return ans;
}
int fac[p],inv[p];
void work() { //线性求阶乘及其逆元,有兴趣的话可以看我之前的博文
fac[0]=fac[1]=inv[0]=1;
for (int i=2; i<p; ++i) fac[i]=1ll*fac[i-1]*i%p;
inv[p-1]=_power(fac[p-1],p-2);
for (int i=p-2; i; --i) inv[i]=1ll*inv[i+1]*(i+1)%p;
}
inline int C(int n,int m) { //直接计算 n,m<p 的情况
if (m>n) return 0;
return 1ll*inv[m]*inv[n-m]%p*fac[n]%p;
}
int Lucas(int n,int m) { //卢卡斯处理 n,m>=p 的情况
if (!m) return 1;
return 1ll*Lucas(n/p,m/p)*C(n%p,m%p)%p;
}
int T,n,l,r;
int main() {
work();
scanf("%d",&T);
while (T--) {
scanf("%d%d%d",&n,&l,&r);
int t=r-l+1; int ans=Lucas(n+t,t);
//或者 int t=r-l; int ans=Lucas(n+t+1,t+1);
printf("%d\n",ans?ans-1:p-1); //注意ans=0时ans-1为-1,需要特殊处理
}
return 0;
}
我的代码中出现了很多
1
l
l
×
⋯
%
p
1ll\times \cdots \%p
1ll×⋯%p 格式的表达式。它计算的就是
⋯
%
p
\cdots\%p
⋯%p 这部分,但乘法可能会爆
i
n
t
\rm int
int ,所以在前面乘上 1ll
进行类型转换。
讲两件事:
- 强制类型转换是最慢的运算。
1ll*...
比(ll)...
快一些。 - i n t \rm int int 比 long long \text{long long} long long 快。这个很好理解, long long \text{long long} long long 位数多所以常数大。