#define PROBLEM "https://judge.yosupo.jp/problem/aplusb"
#include<cassert>
#include<iostream>
#include<memory>
#include<string>
#include<utility>
#include<vector>#include"../../heuristic/beam_search.hpp"
#include"../../utilities/random.hpp"usingm1une::heuristic::Objective;usingm1une::heuristic::beam_search;autodigit_expander(intradix){return[radix](conststd::string&state,int,autoemit){for(intdigit=0;digit<radix;digit++){std::stringcandidate=state;candidate.push_back(static_cast<char>('0'+digit));emit(std::move(candidate));}};}autodigit_range_expander(intradix){return[radix](conststd::string&state,int){std::vector<std::string>result;result.reserve(radix);for(intdigit=0;digit<radix;digit++){result.push_back(state+static_cast<char>('0'+digit));}returnresult;};}intdigit_value(conststd::string&state){intresult=0;for(chardigit:state)result=result*10+digit-'0';returnresult;}voidtest_objectives_and_statistics(){automaximize=beam_search(std::string(),3,2,digit_range_expander(3),digit_value,Objective::maximize);assert(maximize.state=="222");assert(maximize.score==222);assert(maximize.depth==3);assert(maximize.expanded_states==5);assert(maximize.generated_states==15);autominimize=beam_search(std::string(),3,2,digit_range_expander(3),digit_value,Objective::minimize);assert(minimize.state=="000");assert(minimize.score==0);}voidtest_early_stop_and_zero_depth(){autoexpand_once=[](constint&state,intdepth,autoemit){if(depth==1){emit(state+3);emit(state+5);}};autoidentity=[](constint&state){returnstate;};autostopped=beam_search(0,10,4,expand_once,identity);assert(stopped.state==5);assert(stopped.depth==1);assert(stopped.expanded_states==3);assert(stopped.generated_states==2);autozero=beam_search(7,0,1,expand_once,identity);assert(zero.state==7);assert(zero.score==7);assert(zero.depth==0);assert(zero.expanded_states==0);autoexpand_without_depth=[](constint&state){returnstd::vector<int>{state+1,state+2};};autono_depth=beam_search(0,3,1,expand_without_depth,identity);assert(no_depth.state==6);assert(no_depth.depth==3);}structMoveOnlyState{std::unique_ptr<int>value;explicitMoveOnlyState(intvalue_):value(std::make_unique<int>(value_)){}MoveOnlyState(MoveOnlyState&&)=default;MoveOnlyState&operator=(MoveOnlyState&&)=default;MoveOnlyState(constMoveOnlyState&)=delete;MoveOnlyState&operator=(constMoveOnlyState&)=delete;};voidtest_move_only_state(){autoexpand=[](constMoveOnlyState&state,int,autoemit){emit(MoveOnlyState(*state.value+1));emit(MoveOnlyState(*state.value+2));};autoevaluate=[](constMoveOnlyState&state){return*state.value;};autoresult=beam_search(MoveOnlyState(0),4,2,expand,evaluate);assert(*result.state.value==8);assert(result.score==8);}voidtest_randomized_against_exhaustive_search(){m1une::utilities::Randomrandom(0xbea45eaULL);for(inttrial=0;trial<200;trial++){intdepth_limit=int(random.uniform(1,5));intradix=int(random.uniform(2,4));boolmaximize=random.uniform(0,1)!=0;Objectiveobjective=maximize?Objective::maximize:Objective::minimize;intstate_count=1;for(intdepth=0;depth<depth_limit;depth++){state_count*=radix;}autoresult=beam_search(std::string(),depth_limit,state_count,digit_expander(radix),digit_value,objective);intexpected=0;if(maximize){for(intdepth=0;depth<depth_limit;depth++){expected=expected*10+radix-1;}}assert(result.score==expected);}}intmain(){test_objectives_and_statistics();test_early_stop_and_zero_depth();test_move_only_state();test_randomized_against_exhaustive_search();longlonga,b;std::cin>>a>>b;std::cout<<a+b<<'\n';}
#line 1 "verify/heuristic/beam_search.test.cpp"
#define PROBLEM "https://judge.yosupo.jp/problem/aplusb"
#include<cassert>
#include<iostream>
#include<memory>
#include<string>
#include<utility>
#include<vector>#line 1 "heuristic/beam_search.hpp"
#include<algorithm>
#line 6 "heuristic/beam_search.hpp"
#include<concepts>
#include<cstddef>
#include<functional>
#include<type_traits>
#line 12 "heuristic/beam_search.hpp"
#line 1 "heuristic/objective.hpp"
namespacem1une{namespaceheuristic{enumclassObjective{minimize,maximize,};template<classScore>boolbetter_score(constScore&first,constScore&second,Objectiveobjective){if(objective==Objective::maximize)returnsecond<first;returnfirst<second;}}// namespace heuristic}// namespace m1une#line 14 "heuristic/beam_search.hpp"
namespacem1une{namespaceheuristic{template<classState,classScore>structBeamSearchResult{Statestate;Scorescore;intdepth;std::size_texpanded_states;std::size_tgenerated_states;};namespacebeam_search_detail{template<classState,classScore>structNode{Statestate;Scorescore;std::size_torder;};template<classState,classScore>structBetterNode{Objectiveobjective;booloperator()(constNode<State,Score>&first,constNode<State,Score>&second)const{if(better_score(first.score,second.score,objective))returntrue;if(better_score(second.score,first.score,objective))returnfalse;returnfirst.order<second.order;}};}// namespace beam_search_detail// expand(state, next_depth) may return a range of children. For allocation-free// generation, expand(state, next_depth, emit) may instead call emit(child).// evaluate(state) returns its score. The best beam_width states are retained at// every depth, and the best state in the last non-empty layer is returned.template<classState,classExpand,classEvaluate>autobeam_search(Stateinitial_state,intdepth_limit,intbeam_width,Expandexpand,Evaluateevaluate,Objectiveobjective=Objective::maximize){assert(0<=depth_limit);assert(0<beam_width);usingScore=std::remove_cvref_t<std::invoke_result_t<Evaluate&,constState&>>;usingNode=beam_search_detail::Node<State,Score>;usingBetter=beam_search_detail::BetterNode<State,Score>;Scoreinitial_score=std::invoke(evaluate,initial_state);std::vector<Node>beam;beam.push_back(Node{std::move(initial_state),std::move(initial_score),0});std::size_texpanded_states=0;std::size_tgenerated_states=0;intreached_depth=0;if(depth_limit<0||beam_width<=0)depth_limit=0;Betterbetter{objective};for(intnext_depth=1;next_depth<=depth_limit;next_depth++){std::vector<Node>candidates;candidates.reserve(static_cast<std::size_t>(beam_width));std::size_torder=0;for(constNode&node:beam){expanded_states++;autoemit=[&](auto&&candidate_state){usingCandidate=decltype(candidate_state);static_assert(std::is_constructible_v<State,Candidate>);Statestate(std::forward<Candidate>(candidate_state));Scorecandidate_score=std::invoke(evaluate,state);Nodecandidate{std::move(state),std::move(candidate_score),order++};generated_states++;if(int(candidates.size())<beam_width){candidates.push_back(std::move(candidate));std::push_heap(candidates.begin(),candidates.end(),better);}elseif(better(candidate,candidates.front())){std::pop_heap(candidates.begin(),candidates.end(),better);candidates.back()=std::move(candidate);std::push_heap(candidates.begin(),candidates.end(),better);}};ifconstexpr(std::invocable<Expand&,constState&,int>){autonext_states=std::invoke(expand,node.state,next_depth);for(auto&candidate_state:next_states){emit(std::move(candidate_state));}}elseifconstexpr(std::invocable<Expand&,constState&>){autonext_states=std::invoke(expand,node.state);for(auto&candidate_state:next_states){emit(std::move(candidate_state));}}else{std::invoke(expand,node.state,next_depth,emit);}}if(candidates.empty())break;beam=std::move(candidates);reached_depth=next_depth;}intbest=0;for(intindex=1;index<int(beam.size());index++){if(better(beam[index],beam[best]))best=index;}returnBeamSearchResult<State,Score>{std::move(beam[best].state),std::move(beam[best].score),reached_depth,expanded_states,generated_states};}}// namespace heuristic}// namespace m1une#line 1 "utilities/random.hpp"
#line 6 "utilities/random.hpp"
#include<chrono>
#line 8 "utilities/random.hpp"
#include<cstdint>
#line 10 "utilities/random.hpp"
#include<numeric>
#include<queue>
#include<random>
#line 14 "utilities/random.hpp"
#include<string_view>
#include<tuple>
#line 17 "utilities/random.hpp"
#include<unordered_set>
#line 20 "utilities/random.hpp"
namespacem1une{namespaceutilities{structRandomGraphOptions{booldirected=false;boolallow_self_loops=false;boolallow_parallel_edges=false;};structRandom{private:std::mt19937_64_engine;staticunsignedlonglongchrono_seed(){returnstatic_cast<unsignedlonglong>(std::chrono::steady_clock::now().time_since_epoch().count());}staticstd::uint64_tgraph_edge_count(intvertex_count,constRandomGraphOptions&options){std::uint64_tn=static_cast<unsignedint>(vertex_count);if(options.directed){returnoptions.allow_self_loops?n*n:n*(n-1);}returnoptions.allow_self_loops?n*(n+1)/2:n*(n-1)/2;}staticstd::pair<int,int>decode_graph_edge(std::uint64_tindex,intvertex_count,constRandomGraphOptions&options){std::uint64_tn=static_cast<unsignedint>(vertex_count);if(options.directed){std::uint64_twidth=options.allow_self_loops?n:n-1;intfrom=int(index/width);intoffset=int(index%width);intto=options.allow_self_loops||offset<from?offset:offset+1;return{from,to};}autoprefix=[&](std::uint64_tvertex){if(options.allow_self_loops){returnvertex*(2*n-vertex+1)/2;}returnvertex*(2*n-vertex-1)/2;};std::uint64_tlow=0;std::uint64_thigh=n;while(low+1<high){std::uint64_tmiddle=(low+high)/2;if(prefix(middle)<=index){low=middle;}else{high=middle;}}intfrom=int(low);intto=from+int(index-prefix(low))+(options.allow_self_loops?0:1);return{from,to};}public:Random():_engine(chrono_seed()){}explicitRandom(unsignedlonglongseed):_engine(seed){}voidseed(unsignedlonglongvalue){_engine.seed(value);}std::mt19937_64&engine(){return_engine;}unsignedlonglongoperator()(){return_engine();}longlonguniform(longlongl,longlongr){returnstd::uniform_int_distribution<longlong>(l,r)(_engine);}unsignedlonglonguniform_unsigned(unsignedlonglongl,unsignedlonglongr){returnstd::uniform_int_distribution<unsignedlonglong>(l,r)(_engine);}doublereal(doublel=0.0,doubler=1.0){returnstd::uniform_real_distribution<double>(l,r)(_engine);}template<std::integralT>requires(!std::same_as<std::remove_cv_t<T>,bool>)std::vector<T>sequence(intsize,Tlower,Tupper){assert(0<=size);assert(lower<=upper);if(size<0||upper<lower)return{};std::vector<T>result(size);ifconstexpr(std::signed_integral<T>){std::uniform_int_distribution<longlong>distribution(static_cast<longlong>(lower),static_cast<longlong>(upper));for(T&value:result)value=static_cast<T>(distribution(_engine));}else{std::uniform_int_distribution<unsignedlonglong>distribution(static_cast<unsignedlonglong>(lower),static_cast<unsignedlonglong>(upper));for(T&value:result)value=static_cast<T>(distribution(_engine));}returnresult;}std::stringstring(intlength,std::string_viewalphabet="abcdefghijklmnopqrstuvwxyz"){assert(0<=length);assert(length==0||!alphabet.empty());if(length<0||(0<length&&alphabet.empty()))return{};std::stringresult(length,'\0');for(char&character:result){character=alphabet[uniform(0,int(alphabet.size())-1)];}returnresult;}std::vector<int>permutation(intsize,intfirst=0){assert(0<=size);if(size<0)return{};std::vector<int>result(size);std::iota(result.begin(),result.end(),first);shuffle(result);returnresult;}// Returns the edges of a uniformly random labeled tree on [0, size).std::vector<std::pair<int,int>>tree(intsize){assert(0<=size);if(size<=1)return{};std::vector<int>prufer=sequence(size-2,0,size-1);std::vector<int>degree(size,1);for(intvertex:prufer)degree[vertex]++;std::priority_queue<int,std::vector<int>,std::greater<int>>leaves;for(intvertex=0;vertex<size;vertex++){if(degree[vertex]==1)leaves.push(vertex);}std::vector<std::pair<int,int>>edges;edges.reserve(size-1);for(intvertex:prufer){intleaf=leaves.top();leaves.pop();edges.emplace_back(leaf,vertex);if(--degree[vertex]==1)leaves.push(vertex);}intfirst=leaves.top();leaves.pop();edges.emplace_back(first,leaves.top());shuffle(edges);for(auto&[from,to]:edges){if(uniform(0,1))std::swap(from,to);}returnedges;}// Returns m random edges on [0, vertex_count). By default the result is// a simple undirected graph without self-loops.std::vector<std::pair<int,int>>graph(intvertex_count,intedge_count,RandomGraphOptionsoptions={}){assert(0<=vertex_count);assert(0<=edge_count);if(vertex_count<0||edge_count<0)return{};if(edge_count==0)return{};assert(0<vertex_count);if(vertex_count==0)return{};if(!options.allow_self_loops){assert(2<=vertex_count||edge_count==0);if(vertex_count<2)return{};}std::vector<std::pair<int,int>>edges;edges.reserve(edge_count);if(options.allow_parallel_edges){for(intedge=0;edge<edge_count;edge++){intfrom=int(uniform(0,vertex_count-1));intto;if(options.allow_self_loops){to=int(uniform(0,vertex_count-1));}else{to=int(uniform(0,vertex_count-2));if(from<=to)to++;}if(!options.directed&&to<from)std::swap(from,to);edges.emplace_back(from,to);}returnedges;}std::uint64_tmaximum=graph_edge_count(vertex_count,options);assert(static_cast<std::uint64_t>(edge_count)<=maximum);if(maximum<static_cast<std::uint64_t>(edge_count))return{};std::unordered_set<std::uint64_t>selected;selected.reserve(static_cast<std::size_t>(edge_count)*2+1);std::vector<std::uint64_t>indices;indices.reserve(edge_count);for(std::uint64_tcurrent=maximum-edge_count;current<maximum;current++){std::uint64_tcandidate=uniform_unsigned(0,current);if(selected.contains(candidate))candidate=current;selected.insert(candidate);indices.push_back(candidate);}for(std::uint64_tindex:indices){edges.push_back(decode_graph_edge(index,vertex_count,options));}returnedges;}std::vector<std::pair<int,int>>directed_graph(intvertex_count,intedge_count,boolallow_self_loops=false){RandomGraphOptionsoptions;options.allow_self_loops=allow_self_loops;returndirected_graph(vertex_count,edge_count,options);}std::vector<std::pair<int,int>>directed_graph(intvertex_count,intedge_count,RandomGraphOptionsoptions){options.directed=true;returngraph(vertex_count,edge_count,options);}// Returns a directed acyclic graph. Vertices are randomly permuted before// every sampled edge is directed forward in that topological order.std::vector<std::pair<int,int>>dag(intvertex_count,intedge_count,RandomGraphOptionsoptions={}){options.directed=false;options.allow_self_loops=false;std::vector<std::pair<int,int>>edges=graph(vertex_count,edge_count,options);std::vector<int>order=permutation(vertex_count);for(auto&[from,to]:edges){from=order[from];to=order[to];}returnedges;}template<std::integralWeight>requires(!std::same_as<std::remove_cv_t<Weight>,bool>)std::vector<std::tuple<int,int,Weight>>weighted_tree(intsize,Weightlower,Weightupper){std::vector<std::pair<int,int>>edges=tree(size);std::vector<Weight>weights=sequence(int(edges.size()),lower,upper);std::vector<std::tuple<int,int,Weight>>result;result.reserve(edges.size());for(intindex=0;index<int(edges.size());index++){result.emplace_back(edges[index].first,edges[index].second,weights[index]);}returnresult;}template<std::integralWeight>requires(!std::same_as<std::remove_cv_t<Weight>,bool>)std::vector<std::tuple<int,int,Weight>>weighted_graph(intvertex_count,intedge_count,Weightlower,Weightupper,RandomGraphOptionsoptions={}){std::vector<std::pair<int,int>>edges=graph(vertex_count,edge_count,options);std::vector<Weight>weights=sequence(int(edges.size()),lower,upper);std::vector<std::tuple<int,int,Weight>>result;result.reserve(edges.size());for(intindex=0;index<int(edges.size());index++){result.emplace_back(edges[index].first,edges[index].second,weights[index]);}returnresult;}template<std::integralWeight>requires(!std::same_as<std::remove_cv_t<Weight>,bool>)std::vector<std::tuple<int,int,Weight>>weighted_directed_graph(intvertex_count,intedge_count,Weightlower,Weightupper,boolallow_self_loops=false){RandomGraphOptionsoptions;options.allow_self_loops=allow_self_loops;returnweighted_directed_graph(vertex_count,edge_count,lower,upper,options);}template<std::integralWeight>requires(!std::same_as<std::remove_cv_t<Weight>,bool>)std::vector<std::tuple<int,int,Weight>>weighted_directed_graph(intvertex_count,intedge_count,Weightlower,Weightupper,RandomGraphOptionsoptions){options.directed=true;returnweighted_graph(vertex_count,edge_count,lower,upper,options);}template<std::integralWeight>requires(!std::same_as<std::remove_cv_t<Weight>,bool>)std::vector<std::tuple<int,int,Weight>>weighted_dag(intvertex_count,intedge_count,Weightlower,Weightupper,RandomGraphOptionsoptions={}){std::vector<std::pair<int,int>>edges=dag(vertex_count,edge_count,options);std::vector<Weight>weights=sequence(int(edges.size()),lower,upper);std::vector<std::tuple<int,int,Weight>>result;result.reserve(edges.size());for(intindex=0;index<int(edges.size());index++){result.emplace_back(edges[index].first,edges[index].second,weights[index]);}returnresult;}template<typenameT>voidshuffle(std::vector<T>&v){std::shuffle(v.begin(),v.end(),_engine);}template<typenameIterator>voidshuffle(Iteratorfirst,Iteratorlast){std::shuffle(first,last,_engine);}template<typenameT>constT&choice(conststd::vector<T>&v){returnv[uniform(0,static_cast<longlong>(v.size())-1)];}};}// namespace utilities}// namespace m1une#line 12 "verify/heuristic/beam_search.test.cpp"
usingm1une::heuristic::Objective;usingm1une::heuristic::beam_search;autodigit_expander(intradix){return[radix](conststd::string&state,int,autoemit){for(intdigit=0;digit<radix;digit++){std::stringcandidate=state;candidate.push_back(static_cast<char>('0'+digit));emit(std::move(candidate));}};}autodigit_range_expander(intradix){return[radix](conststd::string&state,int){std::vector<std::string>result;result.reserve(radix);for(intdigit=0;digit<radix;digit++){result.push_back(state+static_cast<char>('0'+digit));}returnresult;};}intdigit_value(conststd::string&state){intresult=0;for(chardigit:state)result=result*10+digit-'0';returnresult;}voidtest_objectives_and_statistics(){automaximize=beam_search(std::string(),3,2,digit_range_expander(3),digit_value,Objective::maximize);assert(maximize.state=="222");assert(maximize.score==222);assert(maximize.depth==3);assert(maximize.expanded_states==5);assert(maximize.generated_states==15);autominimize=beam_search(std::string(),3,2,digit_range_expander(3),digit_value,Objective::minimize);assert(minimize.state=="000");assert(minimize.score==0);}voidtest_early_stop_and_zero_depth(){autoexpand_once=[](constint&state,intdepth,autoemit){if(depth==1){emit(state+3);emit(state+5);}};autoidentity=[](constint&state){returnstate;};autostopped=beam_search(0,10,4,expand_once,identity);assert(stopped.state==5);assert(stopped.depth==1);assert(stopped.expanded_states==3);assert(stopped.generated_states==2);autozero=beam_search(7,0,1,expand_once,identity);assert(zero.state==7);assert(zero.score==7);assert(zero.depth==0);assert(zero.expanded_states==0);autoexpand_without_depth=[](constint&state){returnstd::vector<int>{state+1,state+2};};autono_depth=beam_search(0,3,1,expand_without_depth,identity);assert(no_depth.state==6);assert(no_depth.depth==3);}structMoveOnlyState{std::unique_ptr<int>value;explicitMoveOnlyState(intvalue_):value(std::make_unique<int>(value_)){}MoveOnlyState(MoveOnlyState&&)=default;MoveOnlyState&operator=(MoveOnlyState&&)=default;MoveOnlyState(constMoveOnlyState&)=delete;MoveOnlyState&operator=(constMoveOnlyState&)=delete;};voidtest_move_only_state(){autoexpand=[](constMoveOnlyState&state,int,autoemit){emit(MoveOnlyState(*state.value+1));emit(MoveOnlyState(*state.value+2));};autoevaluate=[](constMoveOnlyState&state){return*state.value;};autoresult=beam_search(MoveOnlyState(0),4,2,expand,evaluate);assert(*result.state.value==8);assert(result.score==8);}voidtest_randomized_against_exhaustive_search(){m1une::utilities::Randomrandom(0xbea45eaULL);for(inttrial=0;trial<200;trial++){intdepth_limit=int(random.uniform(1,5));intradix=int(random.uniform(2,4));boolmaximize=random.uniform(0,1)!=0;Objectiveobjective=maximize?Objective::maximize:Objective::minimize;intstate_count=1;for(intdepth=0;depth<depth_limit;depth++){state_count*=radix;}autoresult=beam_search(std::string(),depth_limit,state_count,digit_expander(radix),digit_value,objective);intexpected=0;if(maximize){for(intdepth=0;depth<depth_limit;depth++){expected=expected*10+radix-1;}}assert(result.score==expected);}}intmain(){test_objectives_and_statistics();test_early_stop_and_zero_depth();test_move_only_state();test_randomized_against_exhaustive_search();longlonga,b;std::cin>>a>>b;std::cout<<a+b<<'\n';}