1. 程式人生 > >bzoj 4591 [Shoi2015]超能粒子炮·改

bzoj 4591 [Shoi2015]超能粒子炮·改

lucas定理 題目 ios printf %d nor 處理 details won

題目: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; }

bzoj 4591 [Shoi2015]超能粒子炮·改