結果

問題 No.5020 Averaging
ユーザー ぴぃいいいいぴぃいいいい
提出日時 2024-02-27 07:13:22
言語 C++23
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 971 ms / 1,000 ms
コード長 7,789 bytes
コンパイル時間 3,861 ms
コンパイル使用メモリ 194,876 KB
実行使用メモリ 62,548 KB
スコア 30,411,130
最終ジャッジ日時 2024-02-27 07:14:14
合計ジャッジ時間 51,283 ms
ジャッジサーバーID
(参考情報)
judge14 / judge15
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 887 ms
61,752 KB
testcase_01 AC 838 ms
61,752 KB
testcase_02 AC 888 ms
61,752 KB
testcase_03 AC 966 ms
61,752 KB
testcase_04 AC 871 ms
61,752 KB
testcase_05 AC 875 ms
61,752 KB
testcase_06 AC 895 ms
61,752 KB
testcase_07 AC 896 ms
61,752 KB
testcase_08 AC 880 ms
61,752 KB
testcase_09 AC 866 ms
61,752 KB
testcase_10 AC 879 ms
61,752 KB
testcase_11 AC 893 ms
61,752 KB
testcase_12 AC 833 ms
62,436 KB
testcase_13 AC 885 ms
61,752 KB
testcase_14 AC 877 ms
61,752 KB
testcase_15 AC 886 ms
61,752 KB
testcase_16 AC 833 ms
61,752 KB
testcase_17 AC 861 ms
61,752 KB
testcase_18 AC 884 ms
61,752 KB
testcase_19 AC 856 ms
61,752 KB
testcase_20 AC 856 ms
61,752 KB
testcase_21 AC 874 ms
61,752 KB
testcase_22 AC 882 ms
61,752 KB
testcase_23 AC 862 ms
61,752 KB
testcase_24 AC 879 ms
61,752 KB
testcase_25 AC 854 ms
61,752 KB
testcase_26 AC 894 ms
61,752 KB
testcase_27 AC 867 ms
61,752 KB
testcase_28 AC 893 ms
61,752 KB
testcase_29 AC 852 ms
61,752 KB
testcase_30 AC 901 ms
61,752 KB
testcase_31 AC 863 ms
61,752 KB
testcase_32 AC 850 ms
61,752 KB
testcase_33 AC 886 ms
61,752 KB
testcase_34 AC 963 ms
62,424 KB
testcase_35 AC 882 ms
61,752 KB
testcase_36 AC 835 ms
61,752 KB
testcase_37 AC 868 ms
61,752 KB
testcase_38 AC 852 ms
61,752 KB
testcase_39 AC 889 ms
61,752 KB
testcase_40 AC 851 ms
61,752 KB
testcase_41 AC 851 ms
61,752 KB
testcase_42 AC 869 ms
61,752 KB
testcase_43 AC 860 ms
61,752 KB
testcase_44 AC 861 ms
61,752 KB
testcase_45 AC 858 ms
62,548 KB
testcase_46 AC 892 ms
61,752 KB
testcase_47 AC 971 ms
61,752 KB
testcase_48 AC 877 ms
61,752 KB
testcase_49 AC 848 ms
61,752 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#pragma GCC target("avx")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")

#include <utility>
#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>
#include <optional>
#include <unordered_set>
#include <cassert>
#include <chrono>
using namespace std;
using ull = unsigned long long;
using ll = long long;
using vll = vector<ll>;

inline double get_time() {
    using namespace std::chrono;
    return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
double start_time = get_time();

struct IO{
    int n;
    vll a,b;
    IO(){}
    void input(){
        cin >> n;
        a.resize(n);
        b.resize(n);
        for(int i=0;i<n;i++){
            cin >> a[i] >> b[i];
        }
    }
};

//操作を表す構造体
struct Operation{
    size_t x,y;
};
//操作を元に戻すための情報を表す構造体
struct RollbackOperation{
    size_t x,y;
    pair<ll,ll> x_val;
    pair<ll,ll> y_val;
};

struct State{
    IO *io;
    vll a,b;
    //初期状態の生成
    State(IO &_io) : io(&_io){
        a = io->a;
        b = io->b;
    };
    //スコア計算
    ull score(){
        ll diff_a = a[0] - 500000000000000000ll;
        ll diff_b = b[0] - 500000000000000000ll;
        return max(abs(diff_a),abs(diff_b));
    };
    //ハッシュ値計算
    ull hash(){
        ull h = 0;
        for(int i=0;i<io->n;i++){
            h ^= a[i];
            h ^= b[i];
        }
        return h;
    };
    //状態を更新せずスコアとハッシュのみを計算
    pair<ull,ull> try_apply(Operation op, ull score, ull hash){
        if(op.x==0 || op.y==0){
            ll a_ave = (a[op.x] + a[op.y])/2;
            ll b_ave = (b[op.x] + b[op.y])/2;
            ll a_diff = abs(a_ave-500000000000000000ll);
            ll b_diff = abs(b_ave-500000000000000000ll);
            score = max(a_diff,b_diff);
        }
        hash ^= a[op.x];
        hash ^= b[op.x];
        hash ^= a[op.y];
        hash ^= b[op.y];

        return {score,hash};
    };
    //状態を更新する
    //元の状態に戻すための情報を返す
    RollbackOperation apply(Operation op){
        ll a_ave = (a[op.x] + a[op.y])/2;
        ll b_ave = (b[op.x] + b[op.y])/2;
        RollbackOperation rop = {op.x,op.y,{a[op.x],b[op.x]},{a[op.y],b[op.y]}};
        a[op.x] = a_ave;
        b[op.x] = b_ave;
        a[op.y] = a_ave;
        b[op.y] = b_ave;

        return rop;
    }
    //applyから返された情報を元に状態を戻す
    void rollback(RollbackOperation op){
        a[op.x] = op.x_val.first;
        b[op.x] = op.x_val.second;
        a[op.y] = op.y_val.first;
        b[op.y] = op.y_val.second;
    }
};

struct Node;

struct Cand{
    Operation op;
    shared_ptr<Node> parent;
    ull score;
    ull hash;
    ull p; //優先度
};

using Parent = optional<pair<Operation,shared_ptr<Node>>>;
using Children = vector<pair<Operation,weak_ptr<Node>>>;

struct Node{
    Parent parent;
    Children child;
    ull score;
    ull hash;

    Node(Parent parent, Children child, ull score, ull hash):
        parent(parent),child(child),score(score),hash(hash){}
};

// 多スタート用に構造体にまとめておくと楽
struct Tree{
    State state;
    shared_ptr<Node> node;
    ull rank;

    // 注意: depthは深くなっていくごとに-1されていく
    void dfs(vector<Cand>& next_states,bool one,ull& p,ull depth){
        if(depth==0){
            ull score=node->score;
            ull hash=node->hash;

            // 検算
            // assert(score==state.score());
            // assert(hash==state.hash());

            vector<Operation> ops; //次の操作を列挙
            for(int i=0;i<state.io->n;i++){
                for(int j=i+1;j<state.io->n;j++){
                    if(i==j) continue;
                    ops.emplace_back(i,j);
                }
            }

            //次の操作を全部試す
            for(auto const& op:ops){
                auto [next_score,next_hash]=state.try_apply(op,score,hash);
                next_states.emplace_back(Cand{op,node,next_score,next_hash,p});
                p+=1;
            }
        }
        else{
            auto node_backup=node;
            auto child=&node_backup->child;
            // 有効な子だけにする
            child->erase(remove_if(child->begin(),child->end(),[](pair<Operation,weak_ptr<Node>>& x){return x.second.expired();}),child->end());

            bool next_one=one&child->size()==1;
            
            // 定数調整の必要あり
            if(depth==5){
                p=0;
            }
            ++rank;

            for(const auto& [op,ptr]:*child){
                node=ptr.lock();
                RollbackOperation backup=state.apply(op);
                dfs(next_states,next_one,p,depth-1);
                
                if(!next_one){
                    state.rollback(backup);
                }
            }
            
            if(!next_one){
                node=node_backup;
                --rank;
            }
        }
    }
};

vector<Operation> beam(IO &io){
    constexpr ull turn = 50;
    constexpr ull beam_width = 1000;

    State state(io);
    ull score = state.score();
    ull hash = state.hash();

    // 検算
    /* assert(score==state.score());
    assert(hash==state.hash()); */

    Tree tree{move(state), shared_ptr<Node>(new Node(Parent(),Children(),score,hash)),0};

    vector<shared_ptr<Node>> cur{tree.node};
    vector<Cand> next_states;

    unordered_set<ull> hash_set;

    for(ull i=0;i<turn;i++){
        next_states.clear();
        ull tmp = 0;
        tree.dfs(next_states,true,tmp,i-tree.rank);

        #ifndef ONLINE_JUDGE
        cerr << "turn : " << i << " size : " << next_states.size() << endl;
        #endif

        if(i+1!=turn){
            //上位beam_width個だけ残す
            if(next_states.size() > beam_width){
                nth_element(next_states.begin(),next_states.begin()+beam_width,next_states.end(), [](Cand& a, Cand& b){
                    if(a.score == b.score){ return a.p > b.p; }
                    else return a.score < b.score;
                });
                next_states.erase(next_states.begin()+beam_width,next_states.end());
            }
            
            cur.clear();
            hash_set.clear();
            for(const auto&[op,parent,next_score,next_hash,p]:next_states){
                //重複除去
                if(hash_set.count(next_hash)==0){
                    hash_set.insert(next_hash);
                    auto child_ptr=shared_ptr<Node>(new Node(Parent({op,parent}),Children(),score,hash));
                    parent->child.emplace_back(op,weak_ptr<Node>(child_ptr));
                    cur.emplace_back(child_ptr);
                }
            }
        }
    }

    //最良の状態を選択
    ull arg_min = -1;
    ull min = 1ull<<60;

    for(ull i=0;i<next_states.size();i++){
        if(next_states[i].score<=min){
            min=next_states[i].score;
            arg_min=i;
        }
    }
    auto [op,ptr,best_score,_,__]=next_states[arg_min];

    #ifndef ONLINE_JUDGE
    cerr << "best score : " << best_score << endl;
    cerr<<"rank: "<<turn-tree.rank<<endl;
    #endif

    vector<Operation> ret{op};

    // 操作の復元
    while(ptr->parent.has_value()){
        auto [op,parent]=ptr->parent.value();
        ret.emplace_back(op);
        ptr=parent;
    }

    reverse(ret.begin(),ret.end());
    return ret;
}

int main(){
    IO io;
    io.input();
    auto ops = beam(io);
    cout << ops.size() << endl;
    for(auto [x,y]:ops){
        cout << x+1 << " " << y+1 << endl;
    }

    #ifndef ONLINE_JUDGE
    cerr << "time : " << get_time()-start_time << "ms" << endl;
    #endif
}
0