すげー
↓の最後の問題について詳しく書きます
beet-aizu.hatenablog.com
求めるものは、
∵
一つ箱を追加して「s以下」を「ちょうどs」に言い換える
追加した箱以外には一つ以上ボールを入れないといけないので
個のボールを 個の箱に入れる重複組み合わせになる。
ここで、 と置くと、
は Y のm-n次多項式になる。
この多項式は くらいで陽に計算できる
とおく。
最初の式に対して式変形を行うと
となる。
は、多項係数の性質から である。
したがって、
これは を ごとに計算したものを多項式として捉え、それを繰り返し自乗法を用いて 乗することで で求められる。
あとはこれを実装すれば終わり 全体の計算量は になる。
ソースコード
modintは省略
class TrickyInequality { public: using ll = long long; using M = Mint<int>; vector<M> mul(vector<M> as,vector<M> bs){ int sz=as.size()+bs.size()-1; vector<M> cs(sz,0); for(int i=0;i<(int)as.size();i++) for(int j=0;j<(int)bs.size();j++) cs[i+j]+=as[i]*bs[j]; return cs; } int countSolutions(long long s, int t, int n, int m) { int d=m-n; vector<M> vs({1}); for(int i=0;i<d;i++){ vector<M> ws; ws.emplace_back(s-i); ws.emplace_back(-M(1)); vs=mul(vs,ws); } vector<M> sm(d+1,0); for(int i=1;i<=t;i++){ M po(1); for(int j=0;j<=d;j++){ sm[j]+=po; po*=M(i); } } { M res{1}; for(int j=1;j<=d;j++){ res*=M(j); sm[j]/=res; } for(int j=0;j<=d;j++) vs[j]/=res; } vector<M> dp(d+1,M(0)); dp[0]=M(1); { int p=n; while(p){ if(p&1) dp=mul(dp,sm); sm=mul(sm,sm); p>>=1; dp.resize(d+1); sm.resize(d+1); } } M ans{vs[0]*dp[0]}; { M res{1}; for(int j=1;j<=d;j++){ res*=M(j); ans+=vs[j]*dp[j]*res; } } return ans.v; } };