薊畑

Thistleのブログ

JOI2012-sp-Day1 JOI旗

問題文

https://atcoder.jp/contests/joisc2012/tasks/joisc2012_joi_flag

解説から問題文に飛べます

 

問題概要

(2k)*(2k)サイズのマス目があり、これをJOI旗に塗る。

N点の文字は既に決まっていて、この文字を変えるためには1か所につきコスト1がかかる。

全てのマスを塗るための最小コストは何?

 

考察

素直に考えると、それぞれのマス目において4!通りの色の塗り方を試して、小さいマス目のほうに再帰をするという風な解法になるだろう。

ただ、これをしていると計算量が爆発してしまう。

ここで、再帰をしていった先で一文字も決定していない(=どういう風においてもよい)場合を考えると、これは枝狩りをしてよい。

計算の必要のある(=枝狩りをしてはいけない)マスを考えると、それぞれの文字について、K通りの場面にしか現れないので、計算しなくてはならない場面はN*K以下になり、これは十分計算可能である。

よって、再帰の時に、各範囲について、その中に何個のJ,O,Iで決定しているマスがあるか、が判別できればよいことになる。

これは、それぞれの文字が含まれる(2t)*(2t)の範囲を列挙して記録すればよく、mapなどを使えばできる。

注意するべきこととして、毎回愚直に再帰するとTLEするので、メモ化をしないといけない。

ソースコード

#include<bits/stdc++.h>
using namespace std;
#define fs first
#define sc second
#define H pair<int, int>
#define P pair<int, H>
#define Q(a,b,c) make_pair(a,H{b,c})
#define vec vector
#define pb push_back
#define rep(i,n) for(int i=0;i<(n);i++)
const int inf=1e9;

int k, n;
H a[2000];
char b[2000];
map<P, vec<int>>mp2;
map<P, vec<int>>mp;
map<P, int>dp;

signed main() {
    cin >> k >> n;
    rep(i, n) {
        cin >> a[i].fs >> a[i].sc >> b[i];
        a[i].fs--; a[i].sc--;
        int g = 1;
        rep(t, k + 1) {
            int x = a[i].fs / g * g, y = a[i].sc / g * g;
            mp2[Q(x, y, t)].pb(i);
            g *= 2;
        }
    }

    for (auto g : mp2) {
        mp[g.fs] = vec<int>(3, 0);
 
        for (auto f : g.sc) {
            int t = 0;
            if (b[f] == 'O') t = 1;
            else if (b[f] == 'I') t = 2;
            mp[g.fs][t]++;
        }
        vec<int>b(3, 0);
        rep(i, 3)rep(j, 3) {
            if (i != j) b[i] += mp[g.fs][j];
        }
        mp[g.fs] = b;
    }
 
    auto G = [&](int x, int y, int z, int rank)->int {
        if (mp.find(Q(x, y, rank)) == mp.end()) return 0;
        return mp[Q(x, y, rank)][z];
    };

    function<int(int, int, int)>F = [&](int x, int y, int rank)->int {
        if (dp.find(Q(x, y, rank)) != dp.end()) return dp[Q(x, y, rank)];
 
        if (rank == 0) return 0;
 
        if (mp.find(Q(x, y, rank)) == mp.end()) return 0;
 
        int ret = inf;
        vec<int>c; rep(i, 4) c.pb(i);
        vec<H>d = { {0,0},{0,1},{1,0},{1,1} };
        do {
            int sum = 0;
            rep(i, 4) {
                if (c[i] == 0) {
                    sum += F(x + d[i].fs * (1ll << (rank - 1)), y + d[i].sc * (1ll << (rank - 1)), rank - 1);
                }
                else {
                    sum += G(x + d[i].fs * (1ll << (rank - 1)), y + d[i].sc * (1ll << (rank - 1)), c[i] - 1, rank - 1);
                }
            }
            ret=min(ret, sum);
        } while (next_permutation(c.begin(),c.end()));
 
        return  dp[Q(x, y, rank)] = ret;
    };
    cout << F(0, 0, k) << endl;
}