AcWing 1312. 序列统计

AcWing 1312. 序列统计

题目描述:

给定三个正整数 N,L,R,统计长度在 1 到 N 之间,元素大小都在 L 到 R 之间的单调不降序列的数量。
输出答案对 1e6+3 取模的结果。

输入格式
输入第一行包含一个整数 T,表示数据组数。

第二到第 T+1 行每行包含三个整数 N,L,R。

输出格式
输出包含 T 行,每行有一个数字,表示你所求出的答案对 1e6+3 取模的结果。

数据范围
1≤N,L,R≤1e9,
1≤T≤100,
输入数据保证 L≤R。


题解:

写题解之前还得夸一下y总,y总的讲题思路是真的牛。我决定了以后y总就是我男神 。

回归正题来讲一下这题的思路:

第一步:

先得到长度为k时能达到要求的序列数量(两种思路)

思路一:
假设序列中每个数为ai,则 L ≤ a1 ≤ a2 ≤ a3 ≤ …≤ ak ≤ R。由于ai之间只有相对顺序,所以可以映射为 0 ≤ a1 ≤ a2 ≤ a3 ≤ …≤ ak ≤ R-L。但是到了这一步还是无法求得结果,所以接下来这步才是关键(我要开始装B了,哈哈)

令: x1 = a1
x2 = a2 - a1
x3 = a3 - a2

xk = ak - a(k-1)
所以 0 ≤ x1+x2+x3+x4+…+xk ≤ R-L,故又转化为了求小于等于R-L的数分解成k个大于等于0的数的方案数。想必很多人都想到了用隔板法来求方案数,但是这里的是小于等于R-L,而且还有的方案里面序列中包含0,那这又怎么求呢?

因为有的数为xi=0,那么我可以将xi都加1(令yi = xi + 1),所以 k ≤ y1 + y2 + y3 + … + yk ≤ R-L+K

不想看文字描述的可直接看图片:

到这了不就能让隔板法来展现自己的价值了嘛,但是因为是(这是重点),又和隔板法有点不同了,那这又怎么求呢?

等于R-L+k的情况想必大家都知道了,就是C(R-L+k,k-1),那小于等于的情况不就是R-L+k个小球上插入k个挡板嘛,所以最后长度为k的序列的满足条件的个数不就是C(R-L+k,k)了。

思路二:
因为a1 ≤ a2 ≤ a3 ≤ …≤ ak,所以我们可以令:

b1 = a1
b2 = a2 + 1
b3 = a3 + 2

bk = bk + k - 1
这样就让b序列中任意两个数都不会相等了。

图片描述:

那写在序列b的取值则为 L < b1 < b2 < b3 …< bk < R+k-1,那现在方案数不就是C(R+k-1-L+1,k)=C(R-L+k,k)

第二步:

有了每个长度k对应的组合数,那求长度小于等于n的不就累加求和就行了嘛(so easy!)。但是小丑竟是我自己,一看数据范围1e9,我的个乖乖,玩完了,又卡死了!!!!
每当我卡题时,万能的y总从天而降来帮我度过难关。

最后神来之笔请看图片:

最后就是Lucas的模板了

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll M = 1e6 + 3;
ll n, L, R;
ll ksm(ll a, ll b)
{
	ll ans = 1;
	while (b)
	{
		if (b & 1)
			ans = (ans * a) % M;
		b >>= 1;
		a = (a * a) % M;
	}
	return ans;
}
ll C(ll n, ll m)
{
	if (n < m)return 0;
	ll down = 1, up = 1;
	for (ll i = n, j = 1; j <= m; j++, i--)
	{
		up = up * i % M;
		down = down * j % M;
	}
	return up * ksm(down, M - 2) % M;
}
ll Lucas(ll n, ll m)
{
	if (n < M && m < M)return C(n, m);
	else return C(n / M, m / M) * C(n % M, m % M) % M;
}
int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%lld%lld%lld", &n, &L, &R);
		printf("%lld\n", (Lucas(R - L + n + 1, R - L + 1) - 1 + M) % M);
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值