제출 #1162844

#제출 시각아이디문제언어결과실행 시간메모리
1162844tesseract게임 (IOI13_game)C++20
63 / 100
13094 ms240432 KiB
#include "game.h" #include <cassert> #include <forward_list> #include <map> //#include "ezprint.h" #include <iostream> using namespace std; long long gcd2(long long X, long long Y); template<class Key, class T, class Compare, class Allocator> T getOrDefault(const map<Key, T, Compare, Allocator> &m, const Key &k, const T &def) { auto it = m.find(k); return it == m.end() ? def : it->second; } // Intended for nonempty intervals. struct Interval { int L, U; inline bool operator==(const Interval &other) const = default; inline int length() const {return U-L;} inline bool contains(const Interval &x) const { return L <= x.L && x.U <= U; } inline bool contains(int x) const { return L <= x && x < U;} inline bool nonempty() const { return length() > 0; } inline int mid() const { return L + (U-L)/2;} inline Interval leftHalf() const { return Interval {L, mid()};} inline Interval rightHalf() const { return Interval {mid(), U};} }; // Intended for intervals known to intersect. inline Interval intersection(const Interval &x, const Interval &y) { return {max(x.L, y.L), min(x.U, y.U)}; } inline bool intersects(const Interval &x, const Interval &y) { return intersection(x, y).nonempty(); } class LazySegTreeIntervals { public: struct Node { private: Node *left, *right; // The uld (unique lowest descendant) of a node is defined as follows: // uld(n) = // if n has exactly 1 child, then uld(n's child) // else n // // The idea is that for segment tree purposes, uld(n) has the "equivalent content" // of n, and so can be used in place of n. Node *uld; Node() : left(nullptr), right(nullptr), uld(nullptr) {} Node(const Node &) = delete; Node(Node &&) = delete; int nChildren() const { return (left ? 1 : 0) + (right ? 1 : 0);} // Node *uniqueChild() const { // assert(nChildren() == 1); // return left ? left : right; // } public: const Node *getL() const { return left ? left->uld : nullptr; } const Node *getR() const { return right ? right->uld : nullptr; } friend class LazySegTreeIntervals; }; struct NodePtrIvl { Node *node; Interval ivl; NodePtrIvl leftHalf() const { return {node->left, ivl.leftHalf()};} NodePtrIvl rightHalf() const { return {node->right, ivl.rightHalf()};} }; private: NodePtrIvl root; void decompose(const NodePtrIvl &nodePtrIvl, const Interval &queryIvl, forward_list<const Node *> &ids) const { Node *node = nodePtrIvl.node; Interval nodeIvl = nodePtrIvl.ivl; assert(nodeIvl.contains(queryIvl)); if(nodeIvl == queryIvl) { assert(node->uld); ids.push_front(node->uld); return; } auto leftIvl = nodeIvl.leftHalf(); auto rightIvl = nodeIvl.rightHalf(); if(node->left && intersects(leftIvl, queryIvl)) { decompose(nodePtrIvl.leftHalf(), intersection(leftIvl, queryIvl), ids); } if(node->right && intersects(rightIvl, queryIvl)) { decompose(nodePtrIvl.rightHalf(), intersection(rightIvl, queryIvl), ids); } } void printInternal(const NodePtrIvl &nodePtrIvl, int indent) { for(int i=0; i<2*indent; ++i) { cout << " ";} //ez::println("Node ", nodePtrIvl.node, " ivl ", nodePtrIvl.ivl, " uld ", nodePtrIvl.node->uld); if(nodePtrIvl.node->left) { for(int i=0; i<2*indent; ++i) { cout << " ";} //ez::println("left:"); printInternal(nodePtrIvl.leftHalf(), indent+1); } if(nodePtrIvl.node->right) { for(int i=0; i<2*indent; ++i) { cout << " ";} //ez::println("right:"); printInternal(nodePtrIvl.rightHalf(), indent+1); } } public: inline LazySegTreeIntervals(int N) : root(nullptr, {0,N}) { assert(N >= 1); } struct Updates { forward_list<const Node *> nodes; const Node *pivotOld; const Node *pivotNew; }; // Ensure that all nodes on the path from the root [0,N) to the // leaf [x, x+1) exist. Returns all "relevant" nodes on this path, // that is, those which don't have exactly one child. // The returned list has nodes in the order from leaf to root. Updates ensureNodesAndUpdates(int x) { if(!root.node) { root.node = new Node(); } assert(root.ivl.contains(x)); forward_list<const Node *> ids; NodePtrIvl cur = root; Node *pendingToAssignUld[34]; // Upper bound on height int pendingToAssignUldSize = 0; const Node *pivotOld = nullptr, *pivotNew = nullptr; while(cur.node) { assert(cur.ivl.contains(x)); pendingToAssignUld[pendingToAssignUldSize++] = cur.node; NodePtrIvl nextCur {nullptr, {0,0}}; if(cur.ivl.length() > 1) { int mid = cur.ivl.mid(); if(x < mid) { if (!cur.node->left) { cur.node->left = new Node(); } nextCur = cur.leftHalf(); } else { if (!cur.node->right) { cur.node->right = new Node(); } nextCur = cur.rightHalf(); } } if(cur.node->nChildren() == 2 && cur.node->uld != cur.node) { // cur.node is not a pivot, but is about to become one below. // cur.node->uld is a descendant of cur.node, and its information // in the segment tree must be carried over to cur.node. assert(!pivotOld); assert(!pivotNew); pivotOld = cur.node->uld; pivotNew = cur.node; assert(pivotOld); assert(pivotNew); } if(cur.node->nChildren() != 1) { for(int i=0; i<pendingToAssignUldSize; ++i) { pendingToAssignUld[i]->uld = cur.node; } pendingToAssignUldSize = 0; ids.push_front(cur.node); } cur = nextCur; } return {ids, pivotOld, pivotNew}; } // Try to decompose [a,b) into constituent intervals as // if this was a full segtree. Let X be the set of values for // which createIntervalsAndGetIds has been called. // Consider the ids returned by this method, and // let Y be the union of their corresponding intervals. // Then we have Y intersection X = [a,b) intersection X. // If this was a full segtree, we would have Y = [a,b). // Of course, we also have that the size of the returned list // is at most logarithmic in N. forward_list<const Node *> decomposeIntervalAndGetIds(const Interval &ivl) const { forward_list<const Node *> ids; if(root.node) { decompose(root, ivl, ids); } return ids; } // void print() { // printInternal(root, 0); // } }; LazySegTreeIntervals Rtree(1), Ctree(1); // A key (x,y) corresponds to node with id x in Rtree and // a node with id y in Ctree. Each of those nodes represent an // interval of indices. Their product represents a subrectangle // of cells. The value is the gcd of the values in those cells. typedef const LazySegTreeIntervals::Node * NodeId; struct NodeIdPair { NodeId r, c; bool operator==(const NodeIdPair &) const = default; auto operator<=>(const NodeIdPair &) const = default; }; map<NodeIdPair, long long> gcds; // Key r maps to all nodes in `gcds` whose key have first element r. multimap<NodeId, decltype(gcds)::iterator> gcdsByR; // Key c maps to all nodes in `gcds` whose key have second element c. multimap<NodeId, decltype(gcds)::iterator> gcdsByC; long long & gcdsSquareBracket(NodeId r, NodeId c) { auto [it, wasInserted] = gcds.insert({{r,c}, 0LL}); if(wasInserted) { gcdsByR.insert({r, it}); gcdsByC.insert({c, it}); } return it->second; } void init(int R, int C) { Rtree = LazySegTreeIntervals(R); Ctree = LazySegTreeIntervals(C); } void update(int P, int Q, long long K) { // Each of these is ordered from leaf to root. auto rUpdates = Rtree.ensureNodesAndUpdates(P); auto cUpdates = Ctree.ensureNodesAndUpdates(Q); // First we do some transformations on data structure which // keeps it equivalent, by adding the pivot nodes. //ez::println("\n\n\n\nupdate P=", P, " Q=", Q, " K=", K, "\nRtree"); //Rtree.print(); //ez::println("\nCtree"); //Ctree.print(); //ez::println(); if(rUpdates.pivotOld) { assert(rUpdates.pivotNew); auto [b, e] = gcdsByR.equal_range(rUpdates.pivotOld); assert(b != e); //ez::println("rUpdates.pivotOld ", rUpdates.pivotOld); forward_list<tuple<NodeId, NodeId, long long>> toAddToGcds; for(auto it = b; it != e; ++it) { // if(it->second->first.r != rUpdates.pivotOld) { // error // ez::println("gcds = ", gcds); // ez::println("gcdsByR = ", gcdsByR); // ez::println("gcdsByC = ", gcdsByC); // // ez::println("rUpdates = ", rUpdates); // // ez::println("cUpdates = ", cUpdates); // } //ez::print(" ", it->first); assert(it->first == rUpdates.pivotOld); assert(it->second->first.r == rUpdates.pivotOld); toAddToGcds.emplace_front(rUpdates.pivotNew, it->second->first.c, it->second->second); } for(auto [nodeR, nodeC, val] : toAddToGcds) { gcdsSquareBracket(nodeR, nodeC) = val; } } if(cUpdates.pivotOld) { assert(cUpdates.pivotNew); auto [b,e] = gcdsByC.equal_range(cUpdates.pivotOld); assert(b != e); forward_list<tuple<NodeId, NodeId, long long>> toAddToGcds; for(auto it = b; it != e; ++it) { assert(it->second->first.c == cUpdates.pivotOld); toAddToGcds.emplace_front(it->second->first.r, cUpdates.pivotNew, it->second->second); } for(auto [nodeR, nodeC, val] : toAddToGcds) { gcdsSquareBracket(nodeR, nodeC) = val; } } //Rtree.print(); ////ez::println("\nCtree"); //Ctree.print(); ////ez::println(); forward_list<NodeId> rIds = rUpdates.nodes; forward_list<NodeId> cIds = cUpdates.nodes; assert(!rIds.empty()); assert(!cIds.empty()); //ez::println("rIds ", rIds); //ez::println("cIds ", cIds); for (auto rId : rIds) for (auto cId : cIds) { if (rId == rIds.front() && cId == cIds.front()) { // base case gcdsSquareBracket(rId, cId) = K; continue; } // We have a rectangle which can be split in half in two ways // We can choose either arbitrarily, except for a 1xN or Nx1 // rectangle, in which case there is only one choice. if (rId == rIds.front()) { // cit->c1 or cit->c2 can be nullptr if that child doesn't exist (hasn't been populated) // in the tree. This is not a problem, since in that case that key doesn't exist // in the map, so 0 is returned, as desired. gcdsSquareBracket(rId, cId) = gcd2(getOrDefault(gcds, {rId, cId->getL()}, 0LL), getOrDefault(gcds, {rId, cId->getR()}, 0LL)); } else { gcdsSquareBracket(rId, cId) = gcd2(getOrDefault(gcds, {rId->getL(), cId}, 0LL), getOrDefault(gcds, {rId->getR(), cId}, 0LL)); } } //ez::println("After update ", gcds); } long long calculate(int P, int Q, int U, int V) { auto rIds = Rtree.decomposeIntervalAndGetIds({P, U+1}); auto cIds = Ctree.decomposeIntervalAndGetIds({Q, V+1}); long long ans = 0; for(auto rId : rIds) for(auto cId : cIds) { ans = gcd2(ans, getOrDefault(gcds, {rId, cId}, 0LL)); if (ans==1) { return 1; } } return ans; } inline long long gcd2(long long X, long long Y) { long long tmp; while (X != Y && Y != 0) { tmp = X; X = Y; Y = tmp % Y; } return X; }
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...