BZOJ 4403: 序列统计 (组合数 Lucas 数论推导)

来源:互联网 发布:orcle数据库优化方法 编辑:程序博客网 时间:2024/05/16 01:13

BZOJ 4403: 序列统计

Time Limit: 3 Sec Memory Limit: 128 MB
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

思路:
一道标准的排列组合题目,不过思维很是巧妙,辣鸡的我困扰了许久。
这道题目我们很容易想到一位一位的去考虑,相邻位数之间的递推关系不难发现,但让人崩溃的是1e9的数据规模,递推肯定挂得死死的。虽然无奈但是我们还是确定了思路,一个式子解决问题!
然后。。。额,活生生被搞成了一道数学题。
进过多次尝试后,我绝望地发现,好像并没有什么公式或方法可以直接解决这一问题,那么还是只能对于1~n中的一个i进行分析。我们发现无论我们选出来哪一些数,针对于这些数,有且只有一种排列方式让它满足单调不降(相同数之间交换认为是同一种)。
所以就成了组合问题。如果是单纯的C(m,i),并不包含一种元素可以多选的情况,那如果我们每个元素都取i个呢?->C(m*i,i)显然也是有问题的。eg:2 4 5这一组数据,本来是(4 5)只有4,5 4,4 5,5,现在变成了(4 4 5 5)有4,4 4,5 4,5 4,5 4,5 5,5。为什么呢?因为我们的组合数把两个4,当做了不同的两个数,就多了很多重复的情况。所以说如果我们要添加元素的话,只能增加有数字重复的状态,而不能增加原有的状态。那么我们考虑不去添加数,而是去添加一些符号,来表示这些重复的数字。eg:还是2 4 5这一组数据,我们添加一个+号,来表示它是某个数后边第一个和它相同的数, 4,+ 就代表4,4 ; 5,+就代表5,5;那么我们就有了三个元素(4 5 +);方案就有4,5;4,+;5,+三种,这种方法既解决了有数字重复的情况,又不影响没有数字的情况。
找到了解决方法,做普遍推广,对于i位数,我们添加i-1个符号(最多有i个重复数字),所以方案数就是C(m+i-1,i)。
现在只剩下sigma的问题了,(当然不能for一遍sum)。因为C(m,n)=C(m-1,n)+C(m-1,n-1),在m个东西中取n个,可以分成两类,【(1)不取第一个,那么就是在剩下的m-1个里面取n个。(2)取了第一个,那么就是在剩下的m-1个里面取n-1个。】
我们要求的答案就是,C(m+n-1,n)+C(m+n-2,n-1)+…+C(m+1,2)+C(m,1);
如果我们在式子的末尾添上一个C(m,0);
原式就变成了C(m+n-1,n)+C(m+n-2,n-1)+…+C(m+1,2)+C(m,1)+C(m,0);
因为C(m,1)+C(m,0)=C(m+1,1);
所以原式=C(m+n-1,n)+C(m+n-2,n-1)+…+C(m+1,2)+C(m+1,1);
又因为C(m+1,1)+C(m+1,2)=C(m+2,2);
所以原式=C(m+n-1,n)+C(m+n-2,n-1)+…+C(m+2,2);
…最终ans就是C(m+n,n);
因为我们加上了一个C(m,0);所以最后减掉一个1。
我们求C(m+n,n) - 1!完美!!
然后就是Lucas,逆元求组合数了。

注意一下,因为我们最后要-1,ans可能为负,所以我们做一个处理 ( ans ) % mod + mod ) % mod 。

如果下面的操作还有不懂的话请转至组合数

#include <cstdio>#include <iostream>#define LL long longusing namespace std;const LL mod = 1e6 + 3;LL mub[mod+10];LL x, y;void init(){//初始化阶乘..超过mod    mub[0] = 1;//注意细节    for(int i=1; i<=mod+5; i++){        mub[i] = mub[i-1] * i % mod;    }}LL exgcd(LL a, LL b, LL &x, LL &y){//扩展欧几里得求逆元    if(a == 0 && b == 0)        return -1;    if(b == 0){        x = 1; y = 0;        return a;    }    LL d = exgcd(b, a % b, y, x);        y -= a / b * x;    return d;}LL mod_reverse(LL a, LL n){    LL d = exgcd(a, n, x, y);    if(d == 1)        return ( x % n + n ) % n;    else        return -1;}LL solve(LL a, LL b){    if(a > b) return 0;//    LL nn = mod_reverse((mub[a] * mub[(b + mod - a) % mod]) % mod, mod);    return mub[b] * nn % mod;}void to_solve(LL a, LL b){//Lucas    if(b < mod){        solve(a, b);        return;    }    printf("%lld\n", ( (solve(a/mod, b/mod) * solve(a%mod, b%mod) - 1 ) % mod + mod ) % mod );//}int main(){    int T; scanf("%d", &T);    init();    while ( T-- ){        LL n, l ,r;        scanf("%lld%lld%lld", &n, &l, &r);        LL m = r - l + 1;        if(m + n < mod){            printf("%lld\n", ( ( solve(n, m+n) - 1 ) % mod + mod ) % mod );//        }        else to_solve(n, m+n);    }    return 0;} 
阅读全文
0 0
原创粉丝点击