土下座しながら探索中

主に競技プログラミング

AOJ 2397 : Three-way Branch

問題リンク:Three-way Branch | Aizu Online Judge

問題概要;
H*Wのセルからなるグリッドが存在する
自分は最初セル(1,1)にいて、セル(W,H)に移動する
今いるセルの左下、下、右下に移動できる
いくつかのセル上には障害物が存在しており、そのセルに移動することはできない
セル(W,H)に移動する経路は何通り存在するか?
答えは1000000009でmodをとること
障害物は最大30個存在する

解法:
状態の遷移を行列に落とし繰り返し二乗法を使い計算する

例えば以下の用なグリッドを考える
左上が(1,1)で右下が(W,H)である
'.' はなにもないセルを表し、
'x' は障害物があるセルを表す
b1,b2,b3,b4,b5はそのセルへの経路の総和をもつ行列

.. b1 = [1 0]
.. b2 = [1 1]
.. b3 = [2 2]
.x b4 = [4 0]
.. b5 = [4 4] よって答えは4

まず、1行目から3行目までを考える
1行目から3行目までは以下の行列を使う

A = [1 1] //(1,y)から下、右下に移動できる
    [1 1] //(2,y)から下、右下に移動できる

b2' = A * b1' ('は転置行列)
b3' = A * b2' = A * A * b1'

3行目から4行目のを考える
ここには障害物が存在するため、以下の様な行列を使う

A2 = [1 1] //(1,y)から下、右下に移動できる
     [0 0] //(2,y)からはどこにも移動できない

b4' = A2 * b3' = A2 * A * A * b1'

最後に、4行目から5行目に移動することを考える
これは最初と同様で障害物がないので行列Aを使う

b5' = A * b4' = A * A2 * A * A * b1'

以上の様にして計算する
障害物をy座標毎にまとめておき、
スタート地点から最初の障害物があるy軸までの A のn乗と障害物に対応させた
特別な行列A2を計算し、それを最後まで繰り返す


コード:

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n)   REP(i,0,n)
#define inf (1<<29)
#define MOD 1000000009

using namespace std;
typedef long long ll;
typedef pair<ll,ll> Coor;

typedef vector<ll> VI;
typedef vector<VI> VVI;
typedef VVI matrix;

//行列

ll W,H,N;
int dx[] = {-1,0,+1};
map<ll,vector<int> > obstruction;

matrix getMatrix(vector<int> vec)
{
  bool out[W];
  rep(i,W)out[i] = false;
  rep(i,vec.size())out[vec[i]] = true;
  matrix A = identity(W);
  rep(i,W)A[i][i] = 0;
  rep(x,W)
    {
      if(out[x])continue;
      rep(k,3)
	{
	  int nx = x + dx[k];
	  if(!(0 <= nx && nx < W))continue;
	  A[x][nx] = 1;
	}
    }
  return A;
}

int main()
{
  int CNT = 1;
  while(scanf("%lld %lld %lld",&W,&H,&N),W|H|N)
    {
      obstruction.clear();
      vector<Coor> vec(N);
      vector<ll> b(W,0);
      b[0] = 1;
      bool out = false;
      rep(i,N)
	{
	  scanf("%lld %lld",&vec[i].first,&vec[i].second);
	  vec[i].first--,vec[i].second--;
	  if(vec[i].first == W-1 && vec[i].second == H-1)out = true;
	  if(vec[i].second == 0)continue;
	  if(vec[i].second == H-1)continue;
	  obstruction[vec[i].second].push_back(vec[i].first);
	}

      if(out)
	{
	  cout << "Case " << CNT++ << ": " << 0 << endl;
	  continue;
	}

      matrix A = getMatrix(vector<int>());
      matrix ans = identity(W);

      ll cur = 0;
      for(map<ll,vector<int> >::iterator it = obstruction.begin();it!=obstruction.end();it++)
	{
	  vector<int> obst = it->second;
	  matrix B = getMatrix(obst);
	  ll diff = (it->first) - cur - 1;

	  if(diff != 0)
	    {
	      matrix tmp = pow(A,diff);
	      ans = multi(tmp,ans);
	    }

	  ans = multi(B,ans);
	  cur = (it->first);

	}

      if((H-1) != cur)
	{
	  matrix tmp = pow(A,(H-1)-cur);
	  ans = multi(tmp,ans);
	}

      VI vans = multi(ans,b);
   
      cout << "Case " << CNT++ << ": " << vans[W-1]%MOD << endl;

    }
  return 0;
}