bzoj 4591 [Shoi2015]超能粒子炮·改——组合数前缀和
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4591
先说说自己的想法:
从组合意义的角度考虑,从n个里选<=k个,就添加k个空位置,变成从n+k个里选k个。
其实是错的。因为选空位置的方案数重复了。
于是https://blog.csdn.net/neither_nor/article/details/51684410
其实就是写出∑C的式子,把C用lucas定理表示,发现有一堆 i%mod 相等的东西;
把它们提出来,用乘法可以加速。就像每mod个一个循环节一样。
其实s也很好预处理,因为mod太小了。s也能递归。关键可能是想到可以用s表示。
注意一下k<0的判断。还有jc、ine、jcn(后期的ine)的开始点,还有c和s的不同范围。
#include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; const int mod=2333; int T,ine[mod+5],jc[mod+5],c[mod+5][mod+5],s[mod+5][mod+5]; ll n,k; void init() { ine[1]=1; for(int i=2;i<mod;i++)ine[i]=(mod-mod/i)*ine[mod%i]%mod;//won't use ine[0],or i doesn't have ine under %mod ine[0]=1; for(int i=1;i<mod;i++)(ine[i]*=ine[i-1])%=mod; jc[0]=1;//from 0 for(int i=1;i<mod;i++)jc[i]=jc[i-1]*i%mod; for(int i=0;i<mod;i++) for(int j=0;j<mod;j++)//not j<=i!! { if(j<=i)c[i][j]=jc[i]*ine[j]%mod*ine[i-j]%mod; s[i][j]=c[i][j];if(j)(s[i][j]+=s[i][j-1])%=mod; } } int lucas(ll n,ll m) { if(!m)return 1;if(n<m)return 0;if(n<mod&&m<mod)return c[n][m]; return lucas(n/mod,m/mod)*c[n%mod][m%mod]%mod; } int S(ll n,ll k) { if(k<0)return 0;if(!k)return 1;//if k<0 if(n<mod&&k<mod)return s[n][k]; return (S(n/mod,k/mod-1)*s[n%mod][mod-1]%mod+lucas(n/mod,k/mod)*s[n%mod][k%mod])%mod; } int main() { init(); scanf("%d",&T); while(T--) { scanf("%lld%lld",&n,&k); printf("%d\n",S(n,k)); } return 0; }