// Slow solution for problem E // Author: Grzegorz Herman // // Phase 1: DFS, for every node calculate how much you have to pay so that it // makes sense to enter it and how much you can gain then. // Phase 2: Select a node with the biggest gain, if no such exists check // all choices (backtracking). #include #include #include #include #include #include #include #include using namespace std; #define debug(...) typedef long long weight; const weight INF = numeric_limits::max() / 2; typedef pair gain; struct vertex { weight w, b; gain g; vector N; }; vector G; void dfs(vertex * u, vertex * p = nullptr) { u->b = 0; priority_queue Q; for (vertex * v : u->N) if (v != p) { dfs(v, u); if (v->b > 0) u->b += v->b; if (v->g.second >= 0) Q.push(v->g); } u->g = gain(0, max(u->w, (weight)0)); while (!Q.empty() && u->g.second + Q.top().first >= 0) { u->g.second += Q.top().second; Q.pop(); } u->b += u->w; if (u->w < 0) { u->g.first += u->w; u->g.second += u->w; } while (!Q.empty() && u->g.second <= 0) { u->g.first += Q.top().first; u->g.second += Q.top().second; Q.pop(); } if (u->g.second <= 0) u->g.first = -INF; debug("gain(%ld) = (%+Ld,%+Ld), best = %+Ld\n", u-&G[0], u->g.first, u->g.second, u->b); } typedef pair edge; struct betterg { bool operator()(edge e, edge f) const { return make_pair(e.second->g, e.second) > make_pair(f.second->g, f.second); } }; set Q; // available edges weight cash; bool search(int depth = 0) { weight oldcash = cash; set I, E; // eat everything that pays off while (!Q.empty() && cash + Q.begin()->second->g.first >= 0) { edge e = *Q.begin(); Q.erase(e); E.insert(e); cash += e.second->w; debug("eating %ld, cash = %Ld\n", e.second-&G[0], cash); for (vertex * v : e.second->N) if (v != e.first && v->b > 0) { debug("pushing %ld with gain (%+Ld,%+Ld)\n", v-&G[0], v->g.first, v->g.second); Q.insert(edge(e.second, v)); I.insert(edge(e.second, v)); } } if (!Q.empty()) debug("first in queue (%ld) has gain (%+Ld,%+Ld)\n", Q.begin()->second-&G[0], Q.begin()->second->g.first, Q.begin()->second->g.second); // check for victory if (cash > INF/2) return true; // gather candidates vector C; for (edge e : Q) if (cash + e.second->w >= 0) C.push_back(e); if (C.size() > 1) debug("depth %d: %lu candidates\n", depth, C.size()); // backtrack on each for (edge e : C) { Q.erase(e); cash += e.second->w; for (vertex * v : e.second->N) if (v != e.first && v->b > 0) Q.insert(edge(e.second, v)); //debug("depth %d: trying %ld, cash = %Ld\n", depth, e.second-&G[0], cash); if (search(depth+1)) return true; for (vertex * v : e.second->N) if (v != e.first && v->b > 0) Q.erase(edge(e.second, v)); cash -= e.second->w; Q.insert(e); } // revert changes for (edge e : E) Q.insert(e); for (edge e : I) Q.erase(e); cash = oldcash; return false; } void testcase() { int n, t; assert(2 == scanf("%d%d", &n, &t)); vector(n+1).swap(G); G[0].w = INF; G[0].N.push_back(&G[t]); G[t].N.push_back(&G[0]); for (int i=1; i<=n; ++i) assert(1 == scanf("%Ld", &G[i].w)); for (int i=1; i