解いた問題のソースコードと解説など。


SRM 455 Div2 Hard DonutsOnTheGrid

問題
http://community.topcoder.com/stat?c=problem_statement&pm=10717

'.'か'0'のどちらかの要素を持つグリッドをまず計算する。辺の長さが3以上で辺が全て'0'である長方形の数を求めよ。

頻出問題っぽい。普通にするとO(n^4)で間に合わない。まずはある座標から左側にどれだけ'0'が連続するか求めておく。後に2カラム選んで、その中でyを縦方向に動かす。この時に2行前に辺を張れるならcntを増やしていき、今見ているy行目にも辺が張れると分かった時点でcntを合計値に足していく。

(追記)
2行前に辺が張れるのを調べているのは、ドーナツの穴のサイズが最低でも1x1以上であることから。一行前を調べて辺が張ってあり、今見ている行に辺が張ってあったとしても、この2辺を上辺下辺とする長方形をドーナツにはできない。

こんな感じ↓

i   j
0000
0 . .0
0000 cnt 1 result 1
0000     result 2
0 . .0 cnt 2
0 . .0 cnt 3
0000    result 5
0 . .0

わからなかったのだけれど、kusano_progさんの答えが参考になった。

以下ソース。

int xline[360][360];
int yline[360][360];

class DonutsOnTheGrid {
public:
  long long calc(int H, int W, int seed, int threshold) {
    long long result = 0;
    vector<string> field(H, string(W, '0'));
    ll se = seed;
    for(int i = 0; i < H * W; i++){
      se = (se * 25173 + 13849) % 65536;
      if(se >= threshold) field[i / W][i % W] = '.';
    }
    
    memset(xline, 0, sizeof(xline));
    memset(yline, 0, sizeof(yline));

    //(j, i)からどれだけ'0'が連続するか計算
    for(int i = 0; i < H; i++){
      for(int j = 0; j < W; j++){
	for(int k = 0; k + i < H; k++){
	  if(field[k + i][j] == '0') yline[i][j]++;
	  else break;
	}
	for(int k = 0; k + j < W; k++){
	  if(field[i][k + j] == '0') xline[i][j]++;
	  else break;
	}
      }
    }
    for(int i = 0; i < W; i++){
      for(int j = i + 2; j < W; j++){

	int cnt = 0;

	for(int y = 2; y < H; y++){
	  if(j - i < xline[y - 2][i])
	    cnt++;
	  if(!(field[y - 1][i] == '0' && field[y - 1][j] == '0'))
	    cnt = 0;
	  if(j - i < xline[y][i])
	    result += cnt;
	}
      }
    }
    return result;
  }

これからはしばらくDiv2 Hardをやっていこう。なるべく解いたら公開していきたい。

2週目感想
辺のつながりを前もって計算する部分をboolで計算しており、配列が大きすぎてSIGKILL。また正常に計算できなかったためギブアップ。上のコードを参考にした。
cntの値を蓄積していき、張れるところで今までのまとめて足し上げるのはうまい。

Get up! 明日のSUPER ST@R!