不定期プログラミング覚え書き

青コーダーと黄コーダーの間を彷徨う社会人プロコン勢が余力のあるときに復習した内容をまとめるブログ

SRM541

眠気に負けて不参加だったので,後日EasyMediumだけ解いた.
どちらもre-submitを1回以上要したので,参加してたら痛い目にあっていただろうなあ.
ちなみに250-550-1000回.

Level ProblemName Status Point
Easy AntsMeet PassedSystemTest(Practice) ----
Medium AkariDaisukiDiv1 PassedSystemTest(Practice) ----
Hard XorLife Unopened ----

Easy AntsMeet

問題

アリの初期位置と移動方向の組合せが与えられる.
複数のアリが出会うとそこでアリは消滅することが知られている.
最後まで生き残るアリの数を答えよ

考えたこと

初期位置の範囲が-1000から1000くらいで,移動方向は座標軸に平行な1方向.
つまり2000ステップくらいでアリの数は収束するはずなので,愚直にシミュレーションすれば良い.

と思って書いたら,(0,0)から右に行くアリと(1,0)から左に行くアリの衝突を無視していた/(^o^)\

ならばマイステップ0.5ずつ進むようにすれば,まあ4000ステップのシミュレートだし,なんとかなるよね.

コード
 int countAnts(vector <int> x_, vector <int> y_, string direction) {
    int result;
    bool exist[51];
    vector<double> x ;
    vector<double> y ;
    double dx[51];
    int size = x_.size(); 
    double dy[51];
    memset( exist, true, sizeof( exist ) );
    memset( dx, 0 , sizeof( dx ) );
    memset( dy, 0 , sizeof( dy ) );
    for( int i = 0 ; i < size; i++ ){
      if( direction[i] == 'N' ) dy[i] =0.5;
      if( direction[i] == 'S' ) dy[i] =-0.5;
      if( direction[i] == 'W' ) dx[i] =-0.5;
      if( direction[i] == 'E' ) dx[i] = 0.5;
      x.push_back( x_[i] );
      y.push_back( y_[i] );
    }
    for( int loop = 0 ; loop <= 4000; loop++ ){
      //move
      for( int i = 0 ; i < size; i++ ){
        if( exist[i] ){
          x[i] += dx[i];
          y[i] += dy[i];
        }
      }
      //check
      for( int i = 0 ; i < size; i++ ){
        if( !exist[i] ) continue;
        for( int j = i+1 ; j < size; j++ ){
          if( !exist[j] ) continue;
          if( x[i] == x[j] && y[i] == y[j] ){
            exist[i]= false;
            exist[j] = false;
          }
        }
      }
    }
    result = 0;
    for( int i = 0 ; i < size; i++ ){
      if( exist[i] ) result++;
    }
    return result;
  }

計算量はありの数をNとしてO(4000N^2)
Nが最大50なので許容範囲.

Medium AkariDaisukiDiv1

問題文

文字列Waai,Akari,Daisuki,Sに対して,
関数f(S)を,f(S) = Waai+S+Akari+S+Daisukiと定義する.
 f^k(S)の中に文字列Fが何個含まれているかをmod 1000000007で返せ

考えたこと

問題文の出落ちっぷりがやばい.

説明を呼んで

Sigmar氏の説明(http://topcoder.g.hatena.ne.jp/jackpersel/20120425/1335370326)を読んだら理解した気になった.
ポイントは,Fの長さが50以下であることから,

  • fを50回繰り返したらWaai+...の部分に含まれるFの個数,...+Akari+...に含まれるFの個数,...+Daisukiに含まれるFの個数はすべて定常状態に達する
  • 50回繰り返して無いにしても,Waai,Akari,Daisukiに連結する箇所で覚えるべきなのは,前の文字列の最初50文字&後ろ50文字までで良い

という事らしい.
でもいざ実装してみると結構グダグダになった&細かなミスで3回くらいre-submitした.
本番でこういうの書けるようになりたいなあ

コード
  int countF(string Waai, string Akari, string Daisuki, string S, string F, int k) {
    long long result=0;
    int mod = 1000000007;
    string now = S;
    string first50,last50;
    int Flen = F.size();
    int result_tmp =0;
    int loop;
    for( loop = 0 ; loop < min( 51, k ) ; loop++ ){
      if( now.size() < 51 ){
        now = Waai + now + Akari +now + Daisuki;
        result_tmp = 0;
        for( int i = 0 ; i < (int)now.size(); i++){
          if( now.substr( i, Flen ) == F ){
            result_tmp++;
          }
        }
        result = result_tmp;
        if( (int)now.size() >= 51 ){
          first50 = now.substr(0,50);
          last50 = now.substr( now.size()-50 );
        }
        result %= mod;
      }else{
        // in this case, it is impossible we can find F from Waai+S+Akari and
        // Akari+S+Daisuki.
        // we can just find F from Waai+S, S+Akari+S, or S+Daisuki
        string middle = last50+Akari+first50;
        int last50size = last50.size();
        first50 = Waai + first50;
        last50 = last50 + Daisuki;
        //Waai + ...
        result_tmp = 0;
        for( int i = 0 ; i < (int)Waai.size() ; i++ ){
          if( first50.substr(i,Flen) == F ) result_tmp++;
        }
        // ....+ Daisuki
        for( int i = 0 ; i < (int)Daisuki.size(); i++ ){
          if( last50.substr(last50.size()-Flen-i,Flen) == F ) result_tmp++;
        }
        // ...+Akari+...
        for( int i = 0 ; i < last50size+(int)Akari.size(); i++ ){
          if( i+Flen <= last50size ) continue;
          if( middle.substr( i , Flen ) == F ) result_tmp++;
        }
        result = result*2+result_tmp;
        result %= mod;
        first50 = first50.substr(0,50);
        last50 = last50.substr( last50.size()-50);
      }
    }
    for( ; loop < k ; loop++ ){
      result = result*2 + result_tmp;
      result %= mod;
    }
    return result;
  }