bzoj4403 序列统计——组合数学
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4403
一开始想了个 O(n) 的做法,不行啊...
O(n)想法是这样的:先考虑递推,设 f[i][j] 为在第 i 个位置选第 j 个数字;
设 m = r-l+1;
那么 f[i][j] = ∑(1<=k<=j) f[i-1][k],初值是 f[1][i] = 1 (1<=i<=m);
那么 ans = ∑(1<=i<=n , 1<=j<=m) f[i][j];
这个式子换个角度想,可以考虑初值的1贡献了几次;
对于每个 f[1][j],发现它到 i=2 的位置贡献了 m - j 次,之后每次递推贡献了 ∑(1<=i<=m-j) i 次,也就是 C(m-j , 2) 次;
所以 ans = ∑(1<=i<=m) (C(i,2) * (n-2) + i) + m,其中最后加的 m 是 ∑(1<=i<=m) f[1][i];
但这个时间复杂度是 O(n) 的,不行啊...
然后看了博客:https://blog.csdn.net/Clove_unique/article/details/68491395
竟然是如此简洁!我就是太关注那个序列,其实只需要选出几个数,之后再排序就好了啊!!
需要预处理阶乘逆元,而且别忘了处理 0 的逆元。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; int const mod=1e6+3; int T,n,l,r,m; ll ans,fac[mod+5],inv[mod+5]; ll pw(ll a,ll b) { ll ret=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1)ret=(ret*a)%mod; return ret; } void init() { fac[0]=1; for(int i=1;i<mod;i++) fac[i]=(fac[i-1]*i)%mod; inv[mod-1]=pw(fac[mod-1],mod-2);//不能求 inv[mod] for(int i=mod-2;i>=0;i--) inv[i]=(inv[i+1]*(i+1))%mod;//要处理出0的逆元! } ll C(int n,int m) { if(n<m)return 0; // ll a=1,b=1; m=min(m,n-m); // for(int i=n-m+1;i<=n;i++)a=(a*i)%mod; // for(int i=1;i<=m;i++)b=(b*i)%mod; // return (a*pw(b,mod-2))%mod; return ((fac[n]*inv[m])%mod*inv[n-m])%mod; } ll Lucas(ll n,ll m) { if(m==0)return 1; return (C(n%mod,m%mod)*Lucas(n/mod,m/mod))%mod; } int main() { init(); scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&l,&r); m=r-l+1; ll ans=Lucas((ll)n+m,m)-1; printf("%lld\n",(ans%mod+mod)%mod);// } return 0; }