競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 315D

ABC315D

あまり綺麗な解法がない.全探索によるシュミレーションを高速化する問題.

基本は, 何を全探索するか. 升目の全探索は,削除の処理を考えると TLE である.
たとえば,
aaaaa
bcccc
bdeee
bdfgh
のような形が最悪ケース.

対策:
色が 26種類と少ないことを利用して, 色を全探索する方針. それを行と列に分けて,それぞれ行うが, まずは簡単のため,行と列の一方だけ考えるとよい.
注意
行を消したとき,対応する色の個数も減らす.

もう一つの注意は,行と列を同時に消すということ. 別々に消すと WA. 例えば,Input 2 の様に
aaaaa
abcde
の形が反例.行を先に消すと abcde の 5文字が残るが, 行と列を同時に消すと bcde の 4文字が残る.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  vll vh(2); cinv(vh);
  vector<string> s(vh[0]); cinv(s);
 
  vvvll cnt(2);
  rep(i,2) cnt[i] = vvll(vh[i], vll(26));

  rep(x, vh[0]) rep(y, vh[1]) {
    ll c = s[x][y]-'a';
    cnt[0][x][c]++;
    cnt[1][y][c]++;
  }

  vvll del(2); rep(i,2) del[i] = vll(vh[i]);
  vll rvh = vh;
 
  while(true){
    vector<tuple<ll,ll,ll>> buf;

    //
      // rem: 行と列に同時に印をつけてから消す.
      //  そうしないと,たとえば行だけ先に消すと,
      //  消したせいで 1行になってしまうことがる.
      // 例:Input 2
      //  aaaaa
      //  abcde
      // これは,行を先に消してしまうと 5つ残るが,
      // 行と列を同時に消すと 4つ残る.
   
    rep(k,2) rep(x, vh[k]) if(!del[k][x]){
      rep(c,26){
        if*1{
          buf.push_back({k,x,c});
        }
      }
    }

    if(buf.size()==0) break;

    for(auto [k,x,c]: buf){
      del[k][x] = true;
      rvh[k]--;
      rep(y, vh[k^1]) cnt[k^1][y][c]--; // rem: 実際に削除
    }

  }

  cout << (rvh[0]*rvh[1]) << endl;


  return 0;
}

*1:cnt[k][x][c] == rvh[k^1]) && (rvh[k^1] >=2