beet's soil

競プロのことなど

2013 TCO Algorithm Round 3A - TrickyInequality

すげー

↓の最後の問題について詳しく書きます
beet-aizu.hatenablog.com


求めるものは、
x1=1tx2=1txn=1t(si=1nximn)

一つ箱を追加して「s以下」を「ちょうどs」に言い換える
追加した箱以外には一つ以上ボールを入れないといけないので
s(i=1nxi)(mn) 個のボールを mn+1 個の箱に入れる重複組み合わせになる。


ここで、 Y=i=1nxi と置くと、
f(Y)=(sYmn)=(sY)(sY1)(sY(mn)+1)(mn)! は Y のm-n次多項式になる。
この多項式O((mn)2) くらいで陽に計算できる
f(Y)=k=0mnakYk とおく。


最初の式に対して式変形を行うと
x1=1tx2=1txn=1t(si=1nximn)=k=0mnak(x1=1tx2=1txn=1tYk)=k=0mnak(x1=1tx2=1txn=1t(i=1nxi)k)
となる。


(i=1nxi)k は、多項係数の性質から d1+d2++dn=kk!i=1nxididi! である。

したがって、 x1=1tx2=1txn=1t(i=1nxi)k=x1=1tx2=1txn=1td1+d2++dn=kk!i=1nxididi!=d1+d2++dn=kk!i=1n(xi=1txididi!)

これは (x=1txdd!)d(0dmn) ごとに計算したものを多項式として捉え、それを繰り返し自乗法を用いて n 乗することで O((mn)2logn) で求められる。

あとはこれを実装すれば終わり 全体の計算量は O((mn)2logn) になる。

ソースコード

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