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;
}

 

posted on 2018-07-04 11:32  Narh  阅读(125)  评论(0编辑  收藏  举报

导航