Hatena::Grouptopcoder

cafelier@SRM

cafelier のSRM参加記録です。コンテスト中に考えてたことを執拗に全部書き残すとどうなるだろうかという試み本番中にこういうコードが書きたかったなあ、という後で書いた反省コードを書き残す試み

スパムが来たのでしばらくコメント欄をはてなユーザ限定にしています、すみません、

 | 

2009-11-05

SRM452 1000

| 00:06 | はてなブックマーク - SRM452 1000 - cafelier@SRM

rng_58さんのコメントを参考に。

// mod 演算用ユーティリティ
LL MODVAL = 1000000007LL;
LL ADD(LL x, LL y) { return (x+y)%MODVAL; }
LL SUB(LL x, LL y) { return (x-y+MODVAL)%MODVAL; }
LL MUL(LL x, LL y) { return (x*y)%MODVAL; }
LL POW(LL x, LL e) {
   LL v = 1;
   for(;e;x=MUL(x,x),e>>=1)
      if(e&1)
         v = MUL(v, x);
   return v;
}
LL DIV(LL x, LL y) { return MUL(x, POW(y, MODVAL-2)); }
LL C(LL n, LL k) {
   LL v = 1;
   k = min(k, n-k);
   for(LL i=1; i<=k; ++i)
      v = DIV(MUL(v, n-i+1), i);
   return v;
}

// 本体
class IncreasingNumber { public:
   int countNumbers(long long digits, int divisor) 
   {
      LL  N = digits;
      int D = divisor;

      //  ちょうど N 桁の Increasing number
      //=
      //  0<=Σa<=8 な a[] を使って
      //    1*a[0] + 11*a[1] + ... + 111..111*(a[N-1]+1)
      //  の形で書ける数
      //
      //なので
      //
      //  D で割れるちょうど N 桁の Increasing number の個数
      //=
      //  0<=Σa<=8 な a[] で
      //     coeff[0]*a[0] + coeff[1]*a[1] + ... + coeff[N-1]*(a[N-1]+1) = 0
      //  なものの個数 ( where coeff[i] := "1がi+1個並んだもの" % D )


      // ステージ1

      vector<LL> nc(D); // nc[m] = (coeff[i]%D==m な i の数)
      int       ceLast; // coeff[N-1]
      {
         // coeff は高々 D 回でループしてるはず
         vector<int> coeff;
         int loop_start = -1;
         for(int v=1%D ;;)
         {
            loop_start = find(coeff.begin(), coeff.end(), v) - coeff.begin();
            if( loop_start != coeff.size() )
               break;
            coeff.push_back( v );
            v = (v*10 + 1) % D;
         }
         int loop_len = coeff.size() - loop_start;

         for(int i=0; i<coeff.size() && i<N; ++i)
            if( i < loop_start )
               nc[coeff[i]] = 1;
            else
               nc[coeff[i]] = (N-i+loop_len-1)/loop_len % MODVAL;
         ceLast = N-1 < loop_start ? coeff[N-1]
                                   : coeff[(N-1-loop_start)%loop_len+loop_start];
      }


      // ステージ1.5 (毎回計算してると間に合わなかったのでキャッシュ)

      vector< vector<LL> > choice(D, vector<LL>(9));
      for(int m=0; m<D; ++m)
         for(int n=0; n<=8; ++n)
            // nc[m] 種類のものから n 個取る取り方
            choice[m][n] = (n==0 ? 1 : nc[m]==0 ? 0 : C(n+nc[m]-1, nc[m]-1));


      // ステージ2
      // dp[m][n][q] = 余り [0, 1, ... m] の係数から n 個使って総和を余り q にするやり方は何通り?

      vector< vector< vector<LL> > > dp(D, vector< vector<LL> >(9, vector<LL>(D)));
      for(int m=0; m<D; ++m)
         dp[m][0][ceLast] = 1;
      for(int n=1; n<=8; ++n)
         dp[0][n][ceLast] = choice[0][n];
      for(int m=1; m<D; ++m)
         for(int n=1; n<=8; ++n)
            for(int q=0; q<D; ++q)
               for(int k=0,q2=q; k<=n; ++k,q2=(q2-m+D)%D)
                  dp[m][n][q] = ADD( dp[m][n][q], MUL( dp[m-1][n-k][q2], choice[m][k] ) );

      LL ans = 0;
      for(int n=0; n<=8; ++n)
         ans = ADD(ans, dp[D-1][n][0]);
      return ans;
   }
};
  • なんというか、「10進数表記したときに各桁の数字が昇順に並んでいる」っていう性質を「a + 11b + 111c + 1111d + ... (a + b + ... <= 9) と書ける」って捉え直す考え方がなかったです。なるほどなあ。これは綺麗だ!
  • これに気づかせて貰ったらあとはストレートなDP。
    • っと思ったけど結構時間がシビアなことになってしまった。
    • もっと綺麗に書けるのかな
トラックバック - https://topcoder-g-hatena-ne-jp.jag-icpc.org/cafelier/20091105
 | 

presented by cafelier/k.inaba under CC0