1. 程式人生 > 其它 >[CF1450C2]Errich-Tac-Toe (Hard Version)

[CF1450C2]Errich-Tac-Toe (Hard Version)

壹、題目描述 ¶

傳送門 to Luogu.

貳、題解 ¶

飛雪連天射 \(\rm C1\),卻見彎弓跪 \(\rm C2\),自 \(\rm C1\) 使用一種奇怪的旋轉填法過掉後,我始終認為 \(\rm C2\) 也是可以這樣做的,然而事實是

然而我將剩下的所有事件都拿來肝 \(D\),還是差點時間優化......

事實上,我的 \(\rm C1\) 做法並不能拓展到 \(\rm C2\),想要做 \(\rm C2\) 還需另尋他路。

這種問題,顯然是要對這個格子圖染色,結合經典的做法,我們對於 \((i+j)\bmod 3\) 的餘數入手進行染色,然後我們可以得到這樣的圖:

如果有這樣一張表:

\(\rm Character\)\\((i+j)\bmod 3\) \(0\) \(1\) \(2\)
\(X\) \(x_0\) \(x_1\) \(x_2\)
\(O\) \(o_0\) \(o_1\) \(o_2\)

那首先有 \(k=x_0+x_1+x_2+o_0+o_1+o_2\). 如果令 \(a_{ij}=x_i+o_j\),那麼就有

\[a_{01}+a_{02}+a_{10}+a_{12}+a_{20}+a_{21}=2k \]

顯然,就會存在

\[\min\{a_{01},a_{02},a_{10},a_{12},a_{20},a_{21}\}\le \left\lfloor{2k\over 6}\right\rfloor \]

找到最小值,並替換即可。不過這真的是人類智慧嗎......我並不覺得這方法很容易想到。

叄、參考程式碼 ¶

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;

// #define NDEBUG
#include<cassert>

namespace Elaina{
    #define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
    #define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
    #define fi first
    #define se second
    #define mp(a, b) make_pair(a, b)
    #define Endl putchar('\n')
    #define mmset(a, b) memset(a, b, sizeof a)
    // #define int long long
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    typedef pair<ll, ll> pll;
    template<class T>inline T fab(T x){ return x<0? -x: x; }
    template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
    template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
    template<class T>inline T readin(T x){
        x=0; int f=0; char c;
        while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
        for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
        return f? -x: x;
    }
    template<class T>inline void writc(T x, char s='\n'){
        static int fwri_sta[1005], fwri_ed=0;
        if(x<0) putchar('-'), x=-x;
        do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
        while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
        putchar(s);
    }
}
using namespace Elaina;

const int maxn=300;

char c[maxn+5][maxn+5];
int n;

int a[5][5];

inline void geta(){
    memset(a, 0, sizeof a);
    for(int i=0; i<n; ++i){
        for(int j=0; j<n; ++j) if(c[i][j]!='.'){
            int cur=(i+j)%3;
            if(c[i][j]=='X') rep(t, 0, 2) ++a[cur][t];
            else rep(t, 0, 2) ++a[t][cur];
        }
    }
}

signed main(){
    rep(_, 1, readin(1)){
        n=readin(1);
        for(int i=0; i<n; ++i) scanf("%s", c[i]);
        geta();
        int minn=n*n, x, y;
        rep(i, 0, 2) rep(j, 0, 2) if(i!=j){
            if(a[i][j]<minn)
                minn=a[i][j], x=i, y=j;
        }
        for(int i=0; i<n; ++i) for(int j=0; j<n; ++j) if(c[i][j]!='.'){
            int cur=(i+j)%3;
            /** fill in the opposite color */
            if(cur==x) c[i][j]='O';
            else if(cur==y) c[i][j]='X';
        }
        for(int i=0; i<n; ++i) printf("%s\n", c[i]);
    }
    return 0;
}

肆、關鍵之處 ¶

這個構造方法......只能說 “限制可能會給人以提示” 了罷......另外可能就只有 \((i+j)\bmod 3\) 的餘數這方面可以想到了。