#include #define JUDGE #include #include #include #include #include #include #include #include #include using namespace std; typedef long long int ll; typedef vector> graph; // push_back insert lower_bound upper_bound erase #ifdef JUDGE #define RESULT_OK 42 #define RESULT_WA 43 #define RESULT_JE 2 #else #define RESULT_OK 0 #define RESULT_WA 1 #define RESULT_JE 2 #endif #define DECISION_ATTACK "ATTACK" #define DECISION_DEFEND "DEFEND" #define MAX_TURNS 365 #define INF (1ll<<60) string ROLE="U"; /* Utility functions for writing output validators for the Kattis * problem format. * * The primary functions and variables available are the following. * In many cases, the only functions needed are "init_io", * "wrong_answer", and "accept". * * - init_io(argc, argv): * initialization * * - judge_in, judge_ans, author_out: * std::istream objects for judge input file, judge answer * file, and submission output file. * * - accept(): * exit and give Accepted! * * - accept_with_score(double score): * exit with Accepted and give a score (for scoring problems) * * - judge_message(std::string msg, ...): * printf-style function for emitting a judge message (a * message that gets displayed to a privileged user with access * to secret data etc). * * - wrong_answer(std::string msg, ...): * printf-style function for exitting and giving Wrong Answer, * and emitting a judge message (which would typically explain * the cause of the Wrong Answer) * * - judge_error(std::string msg, ...): * printf-style function for exitting and giving Judge Error, * and emitting a judge message (which would typically explain * the cause of the Judge Error) * * - author_message(std::string msg, ...): * printf-style function for emitting an author message (a * message that gets displayed to the author of the * submission). (Use with caution, and be careful not to let * it leak information!) * */ typedef void (*feedback_function)(const std::string &, ...); const std::string FILENAME_AUTHOR_MESSAGE = "teammessage.txt"; const std::string FILENAME_JUDGE_MESSAGE = "judgemessage.txt"; const std::string FILENAME_JUDGE_ERROR = "judgeerror.txt"; const std::string FILENAME_SCORE = "score.txt"; #define USAGE "%s: judge_in judge_ans feedback_dir < author_out\n" #ifdef JUDGE std::ifstream judge_in, judge_ans; #endif std::istream author_out(std::cin.rdbuf()); char *feedbackdir = NULL; void vreport_feedback(const std::string &category, const std::string &msg, va_list pvar) { std::ostringstream fname; if (feedbackdir) fname << feedbackdir << '/'; fname << category; #ifdef JUDGE FILE *f = fopen(fname.str().c_str(), "a"); assert(f); vfprintf(f, msg.c_str(), pvar); fclose(f); #else vfprintf(stderr, msg.c_str(), pvar); #endif } void report_feedback(const std::string &category, const std::string &msg, ...) { va_list pvar; va_start(pvar, msg); vreport_feedback(category, msg, pvar); } void author_message(const std::string &msg, ...) { va_list pvar; va_start(pvar, msg); vreport_feedback(FILENAME_AUTHOR_MESSAGE, msg, pvar); } void judge_message(const std::string &msg, ...) { va_list pvar; va_start(pvar, msg); vreport_feedback(FILENAME_JUDGE_MESSAGE, msg, pvar); } void wrong_answer(const std::string &msg, ...) { #ifdef JUDGE va_list pvar; va_start(pvar, msg); vreport_feedback(FILENAME_JUDGE_MESSAGE, msg, pvar); #endif #ifdef JUDGE usleep(150000); // needed? #endif exit(RESULT_WA); } void judge_error(const std::string &msg, ...) { #ifdef JUDGE va_list pvar; va_start(pvar, msg); vreport_feedback(FILENAME_JUDGE_ERROR, msg, pvar); #endif assert(0); } void accept() { #ifdef JUDGE usleep(150000); // needed? #endif exit(RESULT_OK); } void accept_with_score(double scorevalue) { report_feedback(FILENAME_SCORE, "%.9le", scorevalue); exit(RESULT_OK); } bool is_directory(const char *path) { struct stat entry; return stat(path, &entry) == 0 && S_ISDIR(entry.st_mode); } void init_io(int argc, char **argv) { #ifdef JUDGE if(argc < 4) { fprintf(stderr, USAGE, argv[0]); judge_error("Usage: %s judgein judgeans feedbackdir [opts] < userout", argv[0]); } // Set up feedbackdir first, as that allows us to produce feedback // files for errors in the other parameters. if (!is_directory(argv[3])) { judge_error("%s: %s is not a directory\n", argv[0], argv[3]); } feedbackdir = argv[3]; judge_in.open(argv[1], std::ios_base::in); if (judge_in.fail()) { judge_error("%s: failed to open %s\n", argv[0], argv[1]); } judge_ans.open(argv[2], std::ios_base::in); if (judge_ans.fail()) { judge_error("%s: failed to open %s\n", argv[0], argv[2]); } author_out.rdbuf(std::cin.rdbuf()); #endif } /////////////////////////////////////////////////////////////////////////////// void wrong_eof(const char* msg) { // if the program crashed, give the runner better chance to notice before we issue WA usleep(150000); wrong_answer(msg); } /////////////////////////////////////////////////////////////////////////////// struct Val{ ll inner; ll guards; bool has_occupied_leaf; ll unoccupied_leaf_id; friend ostream & operator<<(ostream &os, const Val &v){ os << v.inner << ' ' << v.guards << ' ' << v.has_occupied_leaf << ' ' << v.unoccupied_leaf_id; return os; } }; // optimal defending strategy struct Tree{ ll N; ll root; set leaves, inner; set> edges; vector parent; vector> children; vector depth; multiset configuration; ll last_attack; graph g; vector> edge_order; Tree(graph g,set> edges):N(g.size()),root(0),edges(edges),parent(N),children(N),depth(N,-1),g(g){ judge_message("initializing structure\n"); for(ll i=0; i= 2) inner.insert(i); else leaves.insert(i); root = *inner.begin(); { // find centroid to have a nice root (mainly for defending) queue q; vector distance(N,-1); for(ll n : leaves){ distance[n] = 0; q.push(n); } while(!q.empty()){ ll el = q.front(); q.pop(); for(ll n:g[el])if(distance[n] == -1){ distance[n] = distance[el]+1; q.push(n); } } ll mx = -1, mxi=-1; for(ll i=0; i mx){ mx = distance[i]; mxi = i; } } assert(mxi!=-1); root = mxi; judge_message("setting root to %lld\n",root); } if(leaves.begin() != leaves.end()){ last_attack = *leaves.begin(); }else{ last_attack = *inner.begin(); } { // initialize children, parents, & depth of the structure vector todo(N, true); queue q; q.push(root); depth[root] = 0; todo[root] = false; judge_message("structure search\n"); while(!q.empty()){ ll el = q.front(); q.pop(); for(ll n:g[el])if(todo[n]){ q.push(n); todo[n] = false; parent[n] = el; children[el].push_back(n); depth[n] = depth[el] + 1; } } } judge_message("structure dfs\n"); dfs(root,root); ll S = edge_order.size(); for(ll i=0; i inner_v(inner.begin(),inner.end()); // last_attack = inner_v[rand()%inner_v.size()]; last_attack = root; } auto b = leaves.begin(); while((ll)configuration.size() < N && b!=leaves.end()){ configuration.insert(*b); ++b; } } void init(multiset defenders){ configuration = defenders; } vector> defend(ll attack){ if(!(0 <= attack && attack < N)){ judge_message("attack %d out of bounds\n", attack); exit(RESULT_JE); } if(configuration.count(attack)){ judge_message("no movement necessary\n"); return vector>(); // no movement necessary } // find path from the last attack to the new attack vector prev(N, -1); queue q; q.push(attack); prev[attack] = -2; while(!q.empty()){ ll el = q.front(); q.pop(); for(ll n : g[el])if(prev[n]==-1){ q.push(n); prev[n] = el; } } ll last = last_attack; vector path; while(last != -2){ path.push_back(last); last = prev[last]; } judge_message("found path from %lld to %lld with %lld vertices, moving guards along it\n", last_attack, attack, path.size()); last_attack = attack; // move guards along the path so that the new vertex is covered bool encountered_empty_vertex = false; vector> res; for(ll i=0; i+1<(ll)path.size(); ++i){ if(configuration.count(path[i])){ // may move only when a guard is present if(encountered_empty_vertex){ if(depth[path[i]] < depth[path[i+1]]){ // resetting the origin to be near the root if possible res.push_back({path[i],path[i+1]}); } }else{ res.push_back({path[i],path[i+1]}); } }else{ encountered_empty_vertex = true; // empty vertex means that the strategy cannot be optimal, then we should not move vertices as long as they would move towards the root } } return res; } void move(const vector> &moves){ // perform the moves judge_message("defender moved %lld ", moves.size()); for(auto &p:moves) { judge_message(" %lld->%lld", p.first, p.second); } judge_message("\n"); for(auto p:moves) { // each moved guard exists auto it = configuration.find(p.first); if(it == configuration.end()){ wrong_answer("defender is trying to move a non-existant guard %lld\n", p.first); } } for(auto p:moves) { // the guard is moved no more than once auto it = configuration.find(p.first); if(it == configuration.end()){ wrong_answer("defender is trying to move a guard %lld more than once\n", p.first); } configuration.erase(it); } for(auto p:moves) { configuration.insert(p.second); } for(auto &p:moves) { // all moves are along edges if(!edges.count(p)){ wrong_answer("defender moved from %lld to %lld where no edge is present\n", p.first, p.second); } } for(auto &p:configuration) { // end configuration has no doubled guards if(configuration.count(p) > 1){ wrong_answer("defender moved multiple guards onto a single configuration %lld\n", p); } } } ll detect_attack_position(){ map,Val> subtree; for(auto p : edge_order){ ll from = p.first; ll to = p.second; bool is_leaf = leaves.count(from); ll occupied_by_guard = (ll)configuration.count(from); bool is_occupied_leaf = is_leaf && occupied_by_guard != 0; ll unoccupied_leaf_id = is_leaf && !is_occupied_leaf ? from : -1; Val v = {(ll)inner.count(from), occupied_by_guard, is_occupied_leaf, unoccupied_leaf_id}; for(ll n : g[from])if(n!=to){ auto prev_edge = make_pair(n,from); v.inner += subtree[prev_edge].inner; v.guards += subtree[prev_edge].guards; v.has_occupied_leaf |= subtree[prev_edge].has_occupied_leaf; v.unoccupied_leaf_id = max(v.unoccupied_leaf_id, subtree[prev_edge].unoccupied_leaf_id); } if(v.has_occupied_leaf) v.unoccupied_leaf_id = -1; subtree[p] = v; } ll best = -1; ll best_attack = INF; ll weak_point = INF; for(ll i=0; i best){ best = depth[i]; ll to_attack = -1; for(ll nn : g[i])if(nn!=n){ Val &nn_vals = subtree[make_pair(nn, i)]; to_attack = max(to_attack, nn_vals.unoccupied_leaf_id); } best_attack = to_attack; weak_point = i; } } } } } if(best == -1){ // choose random non-occupied vertex set empty_positions; for(ll i=0; i empty_arr(empty_positions.begin(),empty_positions.end()); ll attack; if(empty_arr.size()){ attack = empty_arr[rand()%empty_arr.size()]; }else{ vector confs(configuration.begin(),configuration.end()); attack = confs[rand()%confs.size()]; } judge_message("tough, no winning move; attacking randomly vertex %lld\n", attack); return attack; }else{ judge_message(">>>>>>>>>>>>>>> a weak point found at %lld, attacking optimally\n", weak_point); return best_attack; } } ll optimum(){ return inner.size() + 1; } }; int main(int argc, char **argv){ init_io(argc, argv); judge_message("program running\n"); srand(time(NULL)); ll N, guards; #ifdef JUDGE judge_in >> N >> guards; cout << N << ' ' << guards << endl; #else cin >> N >> guards; #endif // load tree graph g(N); assert(N<=100); set> edges; for(ll i=0; i> A >> B; cout << A << ' ' << B << endl; #else cin >> A >> B; #endif g[A].push_back(B); g[B].push_back(A); edges.insert({A,B}); edges.insert({B,A}); } judge_message("loaded input graph:\n"); judge_message("%lld %lld\n", N, guards); for(auto &p : edges) if(p.first <= p.second) judge_message("%lld %lld\n", p.first, p.second); // resolve optimal strategy Tree tree(g,edges); bool this_program_attacks; #ifdef JUDGE judge_message("judge waiting for decision\n"); string decision; cin >> decision; if(!cin.good()){ wrong_answer("EOF obtained when loading contestant's decision\n"); } if(decision != DECISION_ATTACK && decision != DECISION_DEFEND){ wrong_answer("contestant's decision is malformed, got:%s instead of %s or %s\n", decision.c_str(), DECISION_ATTACK, DECISION_DEFEND); } judge_message("judge obtained decision, contestant decided to play %lld\n", decision); bool contestant_attacks = (decision == DECISION_ATTACK); this_program_attacks = !contestant_attacks; #else ll optimal = tree.optimum(); // decide what role to perform based on the number of provided guards bool should_attack = (guards < optimal); judge_message("C: printing decision\n"); cout << (should_attack ? DECISION_ATTACK : DECISION_DEFEND) << endl; this_program_attacks = should_attack; #endif ROLE = this_program_attacks ? "A" : "D"; if(this_program_attacks){ // ATTACK ////////////////////////////////////////////////////////////////////// judge_message("loading defended vertices\n"); multiset position; for(ll i=0; i> A; position.insert(A); } if(!cin.good()){ wrong_answer("EOF obtained when loading defended vertices\n"); } for(ll A : position) if(A < 0 || N <= A) wrong_answer("guard %lld is out of range [0,%lld)\n", A, N); set unique_guards; for(ll A : position) { if(unique_guards.count(A)) wrong_answer("initial position of guards has more than one guard on vertex %lld\n", A); unique_guards.insert(A); } judge_message("loaded defended vertices"); for(ll n : position) judge_message(" %lld", n); judge_message("\n"); tree.init(position); ll turn; for(turn=0; turn> M; if(!cin.good()){ judge_message("EOF obtained when loading moves; defender is giving up\n"); judge_message("yay! I win; this was expected\n"); accept(); } vector> moves(M); for(auto &p:moves) cin >> p.first >> p.second; if(!cin.good()){ wrong_answer("EOF obtained when loading moves\n"); } // perform the moves tree.move(moves); // check that the moves defended the attacked position if(!tree.configuration.count(attacked_vertex)){ judge_message("the attacked vertex %lld was not defended by the defender moves\n", attacked_vertex); judge_message("current defender configuration is"); for(ll pos : tree.configuration) judge_message(" %lld", pos); judge_message("\n"); #ifdef JUDGE wrong_answer("the contestant was beaten in %lld turns; giving WA\n", turn); #else judge_message("yay! I win; this was expected\n"); accept(); #endif break; } } #ifdef JUDGE judge_message("uff, I did not manage to beat the beaten in %lld turns; seems contestant defending strategy is good\n", turn); cout << -1 << endl; #else judge_error("ERROR: the referential attacking solution was beaten, which should be impossible when it is in the role of the contestant solution\n"); #endif judge_message("ENDING\n"); }else{ // DEFENSE ///////////////////////////////////////////////////////////////////// tree.init(guards); judge_message("printing defended vertices "); bool space = false; for(ll n:tree.configuration){ // print the initial configuration if(space)cout << ' '; space = true; cout << n; judge_message("%lld ", n); } judge_message("\n"); cout << endl; judge_message("defending\n"); ll turn=0, attack; judge_message("waiting for an attack\n"); while(cin >> attack && attack != -1){ judge_message("defending attack on %lld\n", attack); if(turn >= MAX_TURNS){ wrong_answer("contestant did not mannage to beat the defender in %lld moves, he failed; give WA\n", turn); } turn += 1; if(attack < 0 || tree.N <= attack){ wrong_answer("contestant attacked vertex out of bounds: %lld which should have been in [0,%lld)\n", attack, tree.N); } auto moves = tree.defend(attack); tree.move(moves); if(!tree.configuration.count(attack)){ judge_message("I was not able to defend an attack on %lld\n", attack); judge_message("current defender configuration is:"); for(ll pos : tree.configuration) judge_message(" %lld", pos); judge_message("\n"); #ifdef JUDGE judge_message("uff, I was beaten in %lld turns; contestant's solution seems to attack optimally\n", turn); accept(); #else judge_error("ERROR: the referential defensive solution was beaten, which should be impossible when it is in the role of the contestant solution\n"); #endif } cout << moves.size(); for(auto p:moves) cout << " " << p.first << " " << p.second; cout << endl; judge_message("waiting for an attack\n"); } #ifdef JUDGE wrong_answer("the contestant was not able to beat me in %lld turns; giving WA\n", turn); #else judge_message("yay! I win; this was expected\n"); #endif judge_message("ENDING\n"); } accept(); }