제출 #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...