Cod sursa(job #3335680)

Utilizator vldftzFlorea Vlad hackuit vldftz Data 23 ianuarie 2026 10:53:26
Problema Zone Scor 0
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 14.08 kb
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct Triple {
    ll a,b,c;
    bool operator==(Triple const& o) const { return a==o.a && b==o.b && c==o.c; }
};
struct TripleHash { size_t operator()(Triple const& t) const noexcept {
    // simplu hash combinat
    uint64_t x = (uint64_t)t.a * 1000003ULL + (uint64_t)t.b;
    x = x * 1000003ULL + (uint64_t)t.c;
    return (size_t)(x ^ (x>>32));
}};

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N;
    if(!(cin>>N)) return 0;
    vector<ll> S(9);
    for(int i=0;i<9;i++) cin>>S[i];

    vector<vector<ll>> a(N, vector<ll>(N));
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            cin>>a[i][j];
        }
    }

    // precompute column prefix sums: col_pref[r][j] = sum rows 0..r at column j
    vector<vector<ll>> col_pref(N, vector<ll>(N,0));
    for(int r=0;r<N;r++){
        for(int c=0;c<N;c++){
            ll above = (r>0 ? col_pref[r-1][c] : 0);
            col_pref[r][c] = above + a[r][c];
        }
    }
    // total per column
    vector<ll> col_total(N);
    for(int c=0;c<N;c++) col_total[c] = col_pref[N-1][c];

    // precompute row total prefix (sum of entire rows)
    vector<ll> row_sum(N,0), row_pref(N,0);
    for(int r=0;r<N;r++){
        ll s=0;
        for(int c=0;c<N;c++) s += a[r][c];
        row_sum[r]=s;
        row_pref[r] = (r>0? row_pref[r-1] : 0) + s;
    }

    // Generate all unordered partitions of indices 0..8 into three groups of 3
    // Map key = sorted triple sums -> list of partitions (each partition = vector of three vectors of indices)
    struct Part {
        array<int,3> g1, g2, g3; // store indices (sorted inside)
        ll s1,s2,s3;
    };
    map<array<ll,3>, vector<Part>> parts_map;

    vector<int> idx(9);
    iota(idx.begin(), idx.end(), 0);
    // combinations for group1
    for(int i1=0;i1<9;i1++) for(int i2=i1+1;i2<9;i2++) for(int i3=i2+1;i3<9;i3++){
        // group1 = {i1,i2,i3}
        vector<int> rem;
        for(int t=0;t<9;t++){
            if(t==i1 || t==i2 || t==i3) continue;
            rem.push_back(t);
        }
        // choose group2 among rem 6 choose 3
        for(int a=0;a<6;a++) for(int b=a+1;b<6;b++) for(int c=b+1;c<6;c++){
            array<int,3> g1 = {i1,i2,i3};
            array<int,3> g2 = {rem[a], rem[b], rem[c]};
            // g3 is leftover
            vector<int> used(9,0);
            used[i1]=used[i2]=used[i3]=1;
            used[rem[a]]=used[rem[b]]=used[rem[c]]=1;
            array<int,3> g3;
            int p=0;
            for(int t=0;t<9;t++) if(!used[t]) g3[p++]=t;
            sort(g1.begin(), g1.end());
            sort(g2.begin(), g2.end());
            sort(g3.begin(), g3.end());
            // To avoid duplicates of partitions with same groups in different order,
            // ensure lexicographic order of group indices
            vector<array<int,3>> gs = {g1,g2,g3};
            // sort groups lexicographically to create canonical order for partition
            sort(gs.begin(), gs.end(), [](auto const& A, auto const& B){
                if(A[0]!=B[0]) return A[0]<B[0];
                if(A[1]!=B[1]) return A[1]<B[1];
                return A[2]<B[2];
            });
            // compute sums using S
            ll s1 = S[gs[0][0]] + S[gs[0][1]] + S[gs[0][2]];
            ll s2 = S[gs[1][0]] + S[gs[1][1]] + S[gs[1][2]];
            ll s3 = S[gs[2][0]] + S[gs[2][1]] + S[gs[2][2]];
            array<ll,3> key = {s1,s2,s3};
            sort(key.begin(), key.end());
            Part P;
            P.g1 = gs[0]; P.g2 = gs[1]; P.g3 = gs[2];
            P.s1 = s1; P.s2 = s2; P.s3 = s3;
            parts_map[key].push_back(P);
        }
    }

    // Iterate l1 ascending; for each l1 examine all l2 and keep best candidate for that l1.
    bool found_any = false;
    int out_l1=0,out_l2=0,out_c1=0,out_c2=0;

    for(int l1 = 1; l1 <= N-2; ++l1){
        // we will collect best solution for this l1 (according to tie-breakers c1, then l2, then sum)
        bool best_for_l1_exist = false;
        int best_c1 = INT_MAX, best_l2 = INT_MAX, best_c2 = INT_MAX;
        ll best_sum = (1LL<<62);

        for(int l2 = l1+1; l2 <= N-1; ++l2){
            // compute top_total, mid_total, bot_total
            ll top_total = row_pref[l1-1];
            ll mid_total = row_pref[l2-1] - row_pref[l1-1];
            ll bot_total = row_pref[N-1] - row_pref[l2-1];
            array<ll,3> tkey = {top_total, mid_total, bot_total};
            array<ll,3> sortedkey = tkey;
            sort(sortedkey.begin(), sortedkey.end());
            auto it = parts_map.find(sortedkey);
            if(it == parts_map.end()) continue; // no partition of S has these three band sums

            // Build Ptop, Pmid, Pbot prefix arrays across columns (size N: indices 0..N)
            vector<ll> Ptop(N+1,0), Pmid(N+1,0), Pbot(N+1,0);
            for(int c=0;c<N;c++){
                ll top_col = col_pref[l1-1][c];
                ll mid_col = col_pref[l2-1][c] - col_pref[l1-1][c];
                ll bot_col = col_total[c] - col_pref[l2-1][c];
                Ptop[c+1] = Ptop[c] + top_col;
                Pmid[c+1] = Pmid[c] + mid_col;
                Pbot[c+1] = Pbot[c] + bot_col;
            }
            Triple Tot = {Ptop[N], Pmid[N], Pbot[N]}; // should equal (top_total,mid_total,bot_total)

            // dict: V(c) -> list of c where V(c) = (Ptop[c],Pmid[c],Pbot[c]), c in [1..N-1]
            unordered_map<Triple, vector<int>, TripleHash> mapV;
            mapV.reserve(N*2);
            for(int c=1;c<=N-1;c++){
                Triple tv = {Ptop[c], Pmid[c], Pbot[c]};
                mapV[tv].push_back(c); // c ascending insertion => lists sorted
            }

            // For each partition candidate stored under sortedkey, we must consider the actual partition variants
            for(const Part &P : it->second){
                // P has groups in canonical order (g1,g2,g3) but these groups are not labeled top/mid/bottom.
                // We must consider all 6 permutations mapping these three groups to (top,mid,bottom)
                array<array<int,3>,3> groups = {P.g1, P.g2, P.g3};
                array<ll,3> sums = {P.s1, P.s2, P.s3};

                array<int,3> permIdx = {0,1,2};
                for(int perm=0; perm<6; ++perm){
                    int gi_top = permIdx[0], gi_mid = permIdx[1], gi_bot = permIdx[2];
                    // Check sums match exact band totals:
                    ll s_top = sums[gi_top], s_mid = sums[gi_mid], s_bot = sums[gi_bot];
                    if(!(s_top == top_total && s_mid == mid_total && s_bot == bot_total)){
                        // rotate permutation
                        next_permutation(permIdx.begin(), permIdx.end());
                        continue;
                    }
                    // Build arrays of actual numbers for each band (three numbers per band)
                    ll top_vals[3] = { S[ groups[gi_top][0] ], S[ groups[gi_top][1] ], S[ groups[gi_top][2] ] };
                    ll mid_vals[3] = { S[ groups[gi_mid][0] ], S[ groups[gi_mid][1] ], S[ groups[gi_mid][2] ] };
                    ll bot_vals[3] = { S[ groups[gi_bot][0] ], S[ groups[gi_bot][1] ], S[ groups[gi_bot][2] ] };

                    // For speed, we won't sort top_vals etc; we'll use indices 0..2 and handle duplicates.

                    // iterate c1 ascending (1..N-2)
                    for(int c1 = 1; c1 <= N-2; ++c1){
                        Triple A = {Ptop[c1], Pmid[c1], Pbot[c1]};
                        // Check if A.a equals one of top_vals and similarly for mid and bot
                        vector<int> choices_top, choices_mid, choices_bot;
                        for(int t=0;t<3;t++) if(top_vals[t] == A.a) choices_top.push_back(t);
                        if(choices_top.empty()) continue;
                        for(int t=0;t<3;t++) if(mid_vals[t] == A.b) choices_mid.push_back(t);
                        if(choices_mid.empty()) continue;
                        for(int t=0;t<3;t++) if(bot_vals[t] == A.c) choices_bot.push_back(t);
                        if(choices_bot.empty()) continue;

                        // For every choice of indices used for left segment, build remaining indices per band (2 each)
                        for(int itopIdx : choices_top){
                            int remt[2], rp=0;
                            for(int z=0;z<3;z++) if(z!=itopIdx) remt[rp++]=z;
                            for(int imidIdx : choices_mid){
                                int remm[2]; rp=0;
                                for(int z=0;z<3;z++) if(z!=imidIdx) remm[rp++]=z;
                                for(int ibotIdx : choices_bot){
                                    int remb[2]; rp=0;
                                    for(int z=0;z<3;z++) if(z!=ibotIdx) remb[rp++]=z;
                                    // Now choose which of remaining become middle for each band (2*2*2 = 8 combos)
                                    for(int rti=0;rti<2;rti++){
                                        ll mid_top_choice = top_vals[ remt[rti] ];
                                        for(int rmi=0;rmi<2;rmi++){
                                            ll mid_mid_choice = mid_vals[ remm[rmi] ];
                                            for(int rbi=0;rbi<2;rbi++){
                                                ll mid_bot_choice = bot_vals[ remb[rbi] ];
                                                // target V(c2) = V(c1) + (mid_top_choice, mid_mid_choice, mid_bot_choice)
                                                Triple target = { A.a + mid_top_choice, A.b + mid_mid_choice, A.c + mid_bot_choice };
                                                auto itv = mapV.find(target);
                                                if(itv == mapV.end()) continue;
                                                // list of possible c2s (sorted), need c2 > c1 (and c2 <= N-1)
                                                const vector<int> &lst = itv->second;
                                                // binary search for first > c1
                                                auto itpos = std::upper_bound(lst.begin(), lst.end(), c1);
                                                if(itpos == lst.end()) continue;
                                                // for tie-breakers we pick minimal c2 > c1; but there may be multiple c2; pick minimal
                                                int cand_c2 = *itpos;
                                                // compute final right segment C = Tot - V(c2)
                                                Triple Vc2 = { Ptop[cand_c2], Pmid[cand_c2], Pbot[cand_c2] };
                                                Triple C = { Tot.a - Vc2.a, Tot.b - Vc2.b, Tot.c - Vc2.c };
                                                // expected right elements (the third elements per band) are the leftovers (the one not used for left and not used for middle)
                                                // find index of the third element per band
                                                int third_top_idx = 0;
                                                for(int z=0;z<3;z++) if(z!=itopIdx && z!=remt[rti]) third_top_idx = z;
                                                int third_mid_idx = 0;
                                                for(int z=0;z<3;z++) if(z!=imidIdx && z!=remm[rmi]) third_mid_idx = z;
                                                int third_bot_idx = 0;
                                                for(int z=0;z<3;z++) if(z!=ibotIdx && z!=remb[rbi]) third_bot_idx = z;
                                                ll expected_right_top = top_vals[third_top_idx];
                                                ll expected_right_mid = mid_vals[third_mid_idx];
                                                ll expected_right_bot = bot_vals[third_bot_idx];
                                                if(C.a == expected_right_top && C.b == expected_right_mid && C.c == expected_right_bot){
                                                    // Found valid solution (l1,l2,c1,c2)
                                                    // Update best for this l1 according to tie-breaker: minimal c1, then minimal l2, then minimal sum.
                                                    ll curr_sum = (ll)l1 + l2 + c1 + cand_c2;
                                                    if(!best_for_l1_exist
                                                       || make_tuple(c1, l2, curr_sum) < make_tuple(best_c1, best_l2, best_sum)){
                                                        best_for_l1_exist = true;
                                                        best_c1 = c1; best_l2 = l2; best_c2 = cand_c2;
                                                        best_sum = curr_sum;
                                                    }
                                                    // Because we want minimal c1 for this l1 overall, we can stop exploring larger c2 for same c1 & same mapping,
                                                    // but there may be other combinations leading to even smaller c1 (no, c1 fixed now). We continue to check others.
                                                }
                                            }
                                        }
                                    }

                                }
                            }
                        }
                    } // end c1 loop

                    next_permutation(permIdx.begin(), permIdx.end());
                } // end permutations
            } // end candidate partitions

            // After processing this l2, we continue to next l2; we need to explore all l2 to possibly find a smaller c1 for same l1.
        } // end l2 loop

        if(best_for_l1_exist){
            // we found the best solution for the minimal l1; print and exit.
            cout<<l1<<" "<<best_l2<<" "<<best_c1<<" "<<best_c2<<"\n";
            return 0;
        }
    } // end l1 loop

    return 0;
}