bzoj4403:序列统计

我好傻啊

题目

先来看看长度只能为(n)的情况

那么答案非常显然是(inom{m+n-1}{n})

其中(m=R-L+1)

因为我们要构造一个非降序列,显然可能一个数会被选择多次,组合非常不好做,于是我们可以把每一个数的下标加上其对应的下标那么现在的值域范围就变成了([L+1,R+n]),从这个区间里选数的话,我们把选出来的数减去选好之后对应的下标,发现得到的数就来自于原来的([L,R]),于是就变成了从(R+n-L-1+1=n+m-1)里选择的(n)个数,就是(inom{n+m-1}{n})

也可以这样理解,视为把(n)个小球放到(m)个盒子里,这样的话多个小球可以放到同一个盒子里,就对应着一个数可以被选择多次,盒子也可以是空着的,对应着一个数可以不被选择,根据插板法,这样的方案数就是(inom{n+m-1}{m-1}=inom{n+m-1}{n})

现在的问题变成了求

[sum_{i=1}^{n}inom{i+m-1}{m-1} ]

之后画一下柿子就是(inom{n+m}{m}=inom{n+m}{n})

之后上(Lucas)就好了

#include<iostream>
#include<cstring>
#include<cstdio>
#define LL long long
#define re register
#define maxn 1000005
const LL mod=1000003;
inline int read()
{
	char c=getchar();
	int x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9')
		x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
LL fac[maxn],inv[maxn];
inline LL C(LL n,LL m)
{
	if(!m) return 1;
	if(!n) return 0;
	if(n==m) return 1;
	if(m>n) return 0;
	return fac[n]*inv[fac[m]*fac[n-m]%mod]%mod;
}
LL Lucas(LL n,LL m)
{
	if(!m) return 1;
	if(!n) return 0;
	if(n<mod&&m<mod) return C(n,m);
	return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
}
int n,m,T,L,R;
int main()
{
	T=read();
	fac[0]=1;
	for(re int i=1;i<=mod;i++) fac[i]=fac[i-1]*i%mod;
	inv[1]=1;
	for(re int i=2;i<=mod;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	while(T--)
	{
		n=read(),L=read(),R=read();
		m=R-L+1;
		printf("%lld
",(Lucas(n+m,m)-1+mod)%mod);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/asuldb/p/10205701.html