bzoj4403 序列统计
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=4403
题意:
多组数据。
给定三个正整数N、L和R,统计长度在1到N之间,元素大小都在L到R之间的单调不降序列的数量。输出答案对10^6+3取模的结果。
数据范围
1≤N,L,R≤10^9,1≤T≤100
题解:
首先,考虑对于一个长度为n的序列,如何求元素大小都在L到R之间的单调不降序列的数量。
这里提供一个常用转化:
此类单调不降问题常常把第i个数加上i,变成求升序序列的问题。
升序序列,即任选出n个不重复的数,答案是comb(值域,n)
因为已经第i个数加上i,值域从[L,R] 变为 [L+1,R+n]
因此,一个长度为n的序列,元素大小都在L到R之间的单调不降序列的数量就是:
comb(R-L+n,n)
原题转化为:
Σni=1CiR−L+i
=CnR−L+n+Cn−1R−L+n−1+...C2R−L+2+C1R−L+1
=CnR−L+n+Cn−1R−L+n−1+...C2R−L+2+C1R−L+1+1−1
=CnR−L+n+Cn−1R−L+n−1+...C2R−L+2+C1R−L+1+C0R−L+1−1
因为
Cmn=Cmn−1+Cm−1n−1
原式
=CnR−L+n+Cn−1R−L+n−1+...C2R−L+2+C1R−L+2−1
=CnR−L+n+Cn−1R−L+n−1+...C2R−L+3+−1
依次合并,最后得到
CnR−L+n+1−1
直接算即可
(化简式子的问题,添减一项来凑是常用的做法)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int mod=1000003;
const int N=1000010;
int T,n,L,R,fac[N],inv[N];
int C(int n,int m)
{
if(n<m||n<0||m<0) return 0;
if(m==0) return 1;
return ((1LL*(1LL*fac[n]*inv[fac[m]])%mod)*(inv[fac[n-m]]%mod))%mod;
}
int lucas(int n,int m,int p)
{
if(n<m) return 0;
if(m==0) return 1;
return (1LL*(lucas(n/p,m/p,p)%mod)*(C(n%p,m%p)%mod))%mod;
}
void init()
{
fac[0]=1; inv[1]=1; inv[0]=1;
for(int i=1;i<=mod;i++)
{
fac[i]=(1LL*fac[i-1]*i)%mod;
if(i==1) continue;
inv[i]=(1LL*(mod-mod/i)*inv[mod%i])%mod;
}
}
int main()
{
init();
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&L,&R);
printf("%d\n",(lucas(R-L+n+1,n,mod)-1+mod)%mod);
}
return 0;
}