Bzoj4403 序列统计

在洛谷没找到对应的题,不放链接了
然后我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 lxr 的单调不降序列的数量。答案对 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+1xr+len.
显然 [ l + 1 , r + l e n ] [l+1,r+len] [l+1,r+len] 区间内的整数有 r − l + l e n r-l+len rl+len 个。方便起见我们设 t = r − l . t=r-l. t=rl.
从中选出 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=1nCt+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=rl 代回去就有 a n s = C n + r − l + 1 r − l + 1 − 1. ans=C^{r-l+1}_{n+r-l+1}-1. ans=Cn+rl+1rl+11. 这个好办, 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 进行类型转换。

讲两件事:

  1. 强制类型转换是最慢的运算。1ll*...(ll)...快一些。
  2. i n t \rm int int long long \text{long long} long long 快。这个很好理解, long long \text{long long} long long 位数多所以常数大。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值