Hướng dẫn giải của Bedao Regular Contest 16 - DOMINO


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Ta giả sử ~a_i \le b_i~ với mọi ~i~. Khi đó gọi ~w_1, w_2, ..., w_m~ là các tổng có thể tạo được ở hàng 1. Vậy để tìm hiệu bé nhất, ta cần tìm ~w_i~ lớn nhất thỏa mãn ~w_i\le \frac{a_1+a_2+..+a_n+b_1+b_2+...+b_n}{2}~

Mà ~a_i,b_i\le6 \Rightarrow |w_i-w_{i+1}|\le5~. Theo đó, ta chỉ cần duyệt từ ~sum=\frac{a_1+a_2+..+a_n+b_1+b_2+...+b_n}{2}~ giảm dần và kiểm tra có thể tạo được tổng có giá trị như vậy hay không bằng cách giải hệ:

~x_1+2x_2+3x_3+4x_4+5x_5=sum~

~x_1< v_1 + 1,x_2< v_2 + 1,x_3< v_3 + 1,x_4< v_4 + 1,x_5< v_5 + 1~

Trong đó: - ~v_x~ là số vị trí ~i~ mà tại đó ~b_i-a_i=x~

Sử dụng Digit DP:

  • ~f(id, j, carry, mask)~: Có thể tạo được số mà khớp đến chữ số thứ ~id~ của ~sum~ đồng thời số thứ ~j~ trong 5 số; biến nhớ đang nhớ một lượng là ~carry~, và ~mask~ mô tả trạng thái của 5 biến (Bit thứ ~t~ mô tả ~x_t~ đã bé hơn ~v_t+1~ hay chưa)
  • Có thể chứng minh ~carry \le 225~, vì vậy tại một trạng thái mà ~carry \ge 255~ thì ta biết trạng thái đó không thỏa mãn.
  • Gọi ~d~ là số chữ số của ~sum~ và ~x~ là chữ số ở vị trí cao nhất của ~sum~, đáp án là ~f(d - 1,5, x,2^5-1)~

Code mẫu

#include <bits/stdc++.h>
#define BIT(x, i) (((x)>>(i))&1)
#define MASK(i) (1LL<<(i))
#define fi first
#define se second
#define all(x) x.begin(), x.end()
#define discrete(x) x.resize(unique(all(x)) - x.begin())
#define mp make_pair
#define pb push_back
#define TASK "test"

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<pii> vii;
typedef vector<ll> vll;
typedef vector<pll> vlll;

template<typename T1, typename T2> bool mini(T1 &a, T2 b) {if(a>b) a=b; else return 0; return 1;}
template<typename T1, typename T2> bool maxi(T1 &a, T2 b) {if(a<b) a=b; else return 0; return 1;}
template<typename T1> T1 abs(T1 a) {if(a<0) a*=-1; return a;}

mt19937_64 rd(chrono::steady_clock::now().time_since_epoch().count());
ll rand(ll l, ll h){return l + ll(rd()) * rd() %(h-l+1);}

int n;
vector<int> a;
int sum;
vector<vector<int>> v;

struct Answer {
    bool isValid;
    vector<int> ans;

    Answer() {
        isValid = false;
    }

    Answer(bool isValid, vector<int> ans) : isValid(isValid), ans(ans) {}
};

struct DP {
    int n;
    vector<int> sum;
    vector<vector<int>> v;
    map<tuple<int, int, int, int>, bool> dp;

    void init_sum(int x) {
        for(;x;x/=10) sum.pb(x%10);
        n = sum.size();
    }

    void init_v(const vector<vector<int>> &x) {
        vector<int> V;
        v.resize(x.size());
        for(auto k: x) V.push_back(k.size());
        for(int i=0; i<6; i++) {
            for(int j=0; j<n; j++, V[i]/=10) v[i].push_back(V[i]%10);
        }
    }

    DP(int sum, const vector<vector<int>> &v) {
        init_sum(sum);
        init_v(v); 

        // cout<<"sum = "<<sum<<"\n";
        // for(int i=0; i<n; i++) cout<<this->sum[i]<<" \n"[i+1==n];
        // for(int j=0; j<6; j++)
        // {
        //     for(int i=0; i<n; i++) cout<<this->v[j][i]<<" \n"[i+1==n];
        // }
    }

    bool Magic(int i, int j, int carry, int isLeading) {
        if(i == n) return carry == 0 && isLeading == (MASK(6) - 1);
        if(dp.count({i, j, carry, isLeading})) return dp[{i, j, carry, isLeading}];
        if(j == 6) {
            if(carry % 10 == sum[i])
                return dp[{i, j, carry, isLeading}] = Magic(i+1, 0, carry/10, isLeading);
            return dp[{i, j, carry, isLeading}] = false;
        }
        if(BIT(isLeading, j)) {
            return dp[{i, j, carry, isLeading}] = Magic(i, j+1, carry + v[j][i] * j, isLeading);
        }

        bool res = false;
        for(int x = 0; x <= 9 && !res; x++) {
            res |= Magic(i, j+1, carry + x * j, isLeading);
            if(x < v[j][i]) res |= Magic(i, j+1, carry + x * j, isLeading | (MASK(j)));
        }

        return dp[{i, j, carry, isLeading}] = res;
    }

    vector<int> trace(int i, int j, int carry, int isLeading) {
        // cout<<"trace "<<i<<" "<<j<<" "<<carry<<" "<<isLeading<<"\n";
        if(i == n) return vector<int>(6, 0);
        if(j == 6) {
            // cout<<"gone to j == 6\n";
            return trace(i+1, 0, carry/10, isLeading);
        }
        if(BIT(isLeading, j)) {
            // cout<<"gone to BIT(isLeading, j)\n";
            vector<int> res = trace(i, j+1, carry + v[j][i] * j, isLeading);
            res[j] *= 10;
            res[j] += v[j][i];
            // cout<<"res = (from trace("<<i<<", "<<j+1<<", "<<carry + v[j][i] * j<<", "<<isLeading<<"))";
            // for(int &x: res) cout<<x<<" "; cout<<"\n";
            return res;
        }

        for(int x = 0; x <= 9; x++) {
            if(Magic(i, j+1, carry + x * j, isLeading)) {
                // cout<<"gone to Magic(i, j+1, carry + x * j, isLeading) with x = "<<x<<"\n";
                vector<int> res = trace(i, j+1, carry + x * j, isLeading);
                res[j] *= 10;
                res[j] += x;
                // cout<<"res = (from trace("<<i<<", "<<j+1<<", "<<carry + v[j][i] * j<<", "<<isLeading<<"))";
                // for(int &x: res) cout<<x<<" "; cout<<"\n";
                return res;
            }
            if(x < v[j][i] && Magic(i, j+1, carry + x * j, isLeading | (MASK(j)))) {
                // cout<<"gone to Magic(i, j+1, carry + x * j, isLeading | (MASK(j))) with x = "<<x<<"\n";
                vector<int> res = trace(i, j+1, carry + x * j, isLeading | (MASK(j)));
                res[j] *= 10;
                res[j] += x;        
                // cout<<"res = (from trace("<<i<<", "<<j+1<<", "<<carry + v[j][i] * j<<", "<<isLeading<<"))";
                // for(int &x: res) cout<<x<<" "; cout<<"\n";
                return res;
            }
        }
    }

    Answer run() {
        Answer res;
        for(int mask=0; mask < MASK(6); mask++) if(Magic(0, 0, 0, mask)) {
            res = Answer(true, trace(0, 0, 0, mask));
            break;
        }
        return res;
    }
};

void varify(const Answer &res, int s) {
    vector<int> tmp;
    for(int i=0; i<6; i++) {
        for(int j=0; j<res.ans[i]; j++) tmp.push_back(v[i][j]);
    }
    int S = 0;
    for(int x: tmp) S += a[x];
    if(S == s) cout<<"OK\n";
    else cout<<"WRONG\n";
}
vi A, B;

void print_sol(vi tmp) {
    // cout<<"tmp = \n"; for(int x: tmp) cout<<x<<" "; cout<<'\n';
    vi flag(n, 0);
    for(int i=0; i<n; i++) if(A[i] > B[i]) flag[i] = 1;
    // cout<<"flag = \n"; for(int x: flag) cout<<x<<" "; cout<<'\n';
    for(int i=0; i<n; i++) if(flag[i]) swap(A[i], B[i]);
    for(int i=0; i<n; i++) {
        if(tmp[B[i] - A[i]] == 0) continue;
        tmp[B[i] - A[i]]--;
        flag[i] ^= 1;
        swap(A[i], B[i]);
    }

    int top = 0, bot = 0;
    for(int i=0; i<n; i++) {
        top += A[i];
        bot += B[i];
    }
    // cout<<"top = "<<top<<", bot = "<<bot<<'\n';
    vector<int> abc;
    for(int i=0; i<n; i++) if(flag[i]) abc.push_back(i + 1);
    cout << abc.size() << ' ';
    for (int v : abc) cout << v << ' ';
}

void proc(const int &TTT)
{
    v.resize(6);
    cin>>n;
    a.resize(n);
    for(int &x: a) {
        cin>>x;
        A.push_back(x);
    }
    for(int &x: a) {
        int c; cin>>c;
        x = abs(x - c);
        B.push_back(c);
    }
    for(int x: a) sum += x;
    // cout<<"a = \n";
    // for(int x: a) cout<<x<<' '; cout<<'\n';
    // cout<<"sum = "<<sum<<'\n';
    for(int i=0; i<n; i++) v[a[i]].pb(i);
    for(int s = sum / 2; s; s--) {
        Answer res = DP(s, v).run();
        if(res.isValid == false) continue;

        // cout<<"Sum = "<<sum<<'\n';
        // cout<<"s = "<<s<<'\n';
        // for(int i=0; i<6; i++) {
        //     cout<<"res.ans["<<i<<"] = "<<res.ans[i]<<'\n';
        // }

        // varify(res, s);
        cout<<sum - 2 * s<<'\n';
        vi tmp;
        for(int i=0; i<6; i++) {
            for(int j=0; j<res.ans[i]; j++) tmp.pb(v[i][j] + 1);
        }
        print_sol(res.ans);
        return;
    }
}

int main()
{
    // freopen(TASK".inp", "r", stdin);
    // freopen(TASK".out", "w", stdout);
    ios_base::sync_with_stdio(0); cin.tie(0);
    int numTest = 1; 
    // cin>>numTest;
    for(int i=1; i<=numTest; i++) proc(i);
    return 0;
}

Bình luận

Hãy đọc nội quy trước khi bình luận.