【bzoj4403】【序列统计】不降转升+组合数添项合并

这里写图片描述
(上不了p站我要死了,侵权度娘背锅)

Description
给定三个正整数N、L和R,统计长度在1到N之间,元素大小都在L到R之间的单调不降序列的数量。输出答案对10^6+3取模的结果。
Input
输入第一行包含一个整数T,表示数据组数。
第2到第T+1行每行包含三个整数N、L和R,N、L和R的意义如题所述。
1≤N,L,R≤10^9,1≤T≤100,输入数据保证L≤R。
Output
输出包含T行,每行有一个数字,表示你所求出的答案对10^6+3取模的结果。
Sample Input
2
1 4 5
2 4 5
Sample Output
2
5
//【样例说明】满足条件的2个序列为[4]和[5]。

好吧。。。我自己根本想不出来

如果单调不降难以求出来,而单调上升很容易求出来,那么为什么不把单调不降转为单调上升呢?
把第i位加上i,于是就将问题转为了在[l+1,r+n]中选n个不同的数出来,按从小到大的顺序排列。方案数为C(r-l+n,n)。

然后要求长度为1~n的方案数
全部列出来:
这里写图片描述
很是巧妙啊

组合数中的添项拆项(C(n,1)=C(n,n)=1)化项(C(n,n)=C(n+1,n+1))的运用,通常都是向递推式C(n,m)=C(n-1,m)+C(n-1,m-1)看齐。

代码(记得处理负数)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#ifdef WIN32
#define RIN "%I64d"
#else
#define RIN "%lld"
#endif
#define ll long long 

template <typename T>inline void read(T &res){
    T k=1,x=0;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-')k=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    res=k*x;
}

const ll mod=1e6+3;

ll jiec[mod+7],niy[mod+7];
ll n,l,r;

void exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1,y=0;
        return ;
    }
    ll x0,y0;
    exgcd(b,a%b,x0,y0);
    x=y0;
    y=x0-(a/b)*y0;
}
ll inverse(ll a){
    ll x,y;
    exgcd(a,mod,x,y);
    return (x%mod+mod)%mod;
}
void init(){
    jiec[0]=niy[0]=1;
    for(int i=1;i<mod;i++) jiec[i]=jiec[i-1]*i%mod;
    niy[mod-1]=inverse(jiec[mod-1]);
    for(int i=mod-2;i>=1;i--) niy[i]=niy[i+1]*(i+1)%mod;
}
ll comb(ll a,ll b){
    return jiec[a]*niy[b]%mod*niy[a-b]%mod;
}
ll lucas(ll a,ll b){
    if(a<b) return 0;
    if(a==0&&b==0) return 1;
    if(a<mod&&b<mod) return comb(a,b);
    return lucas(a/mod,b/mod)*lucas(a%mod,b%mod)%mod;
}
void solve(){
    read(n),read(l),read(r);
    printf(RIN"\n",(lucas(r-l+n+1,n)-1+mod)%mod);
}
int main(){
    init();
    int t;
    read(t);
    while(t--) solve();
    return 0;
}
posted @ 2017-10-31 19:08  LinnBlanc  阅读(184)  评论(0编辑  收藏  举报